Add FilterSpecs to EventLogsPipeline (#2274)
authorJustin Anderson <jander-msft@users.noreply.github.com>
Wed, 12 May 2021 22:25:16 +0000 (15:25 -0700)
committerGitHub <noreply@github.com>
Wed, 12 May 2021 22:25:16 +0000 (15:25 -0700)
* Add FilterSpecs to EventLogsPipeline.
* Set default value of UseAppFilters to true.
* Add unit tests for all settings on EventLogsPipelineSettings.

eng/Versions.props
src/Microsoft.Diagnostics.Monitoring.EventPipe/Configuration/LogMessageType.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.Monitoring.EventPipe/Configuration/LoggingSourceConfiguration.cs
src/Microsoft.Diagnostics.Monitoring.EventPipe/Logs/EventLogsPipeline.cs
src/Microsoft.Diagnostics.Monitoring.EventPipe/Logs/EventLogsPipelineSettings.cs
src/tests/EventPipeTracee/Program.cs
src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventLogsPipelineUnitTests.cs
src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/Microsoft.Diagnostics.Monitoring.EventPipe.UnitTests.csproj
src/tests/Microsoft.Diagnostics.Monitoring/Microsoft.Diagnostics.Monitoring.UnitTests.csproj

index 808c4f00ab989a346c09584f01a219162402ea9f..52182fc96a814c91c2c2499a5aef4026a109826f 100644 (file)
@@ -41,8 +41,8 @@
     <MicrosoftDiaSymReaderNativePackageVersion>1.7.0</MicrosoftDiaSymReaderNativePackageVersion>
     <MicrosoftDiagnosticsTracingTraceEventVersion>2.0.64</MicrosoftDiagnosticsTracingTraceEventVersion>
     <MicrosoftExtensionsLoggingVersion>2.1.1</MicrosoftExtensionsLoggingVersion>
-    <!-- We use a newer version of LoggingEventSource due to a bug in an older version-->
-    <MicrosoftExtensionsLoggingEventSourceVersion>3.1.4</MicrosoftExtensionsLoggingEventSourceVersion>
+    <!-- Need version that understands UseAppFilters sentinel. -->
+    <MicrosoftExtensionsLoggingEventSourceVersion>5.0.1</MicrosoftExtensionsLoggingEventSourceVersion>
     <SystemCommandLineVersion>2.0.0-beta1.20468.1</SystemCommandLineVersion>
     <SystemCommandLineRenderingVersion>2.0.0-beta1.20074.1</SystemCommandLineRenderingVersion>
     <SystemMemoryVersion>4.5.4</SystemMemoryVersion>
diff --git a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Configuration/LogMessageType.cs b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Configuration/LogMessageType.cs
new file mode 100644 (file)
index 0000000..aaf248d
--- /dev/null
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+
+namespace Microsoft.Diagnostics.Monitoring.EventPipe
+{
+    [Flags]
+    public enum LogMessageType
+    {
+        Message = 0x2,
+        FormattedMessage = 0x4,
+        JsonMessage = 0x8
+    }
+}
index 3b7ed790a47eca332b9b2ce2676e0eed6b48998f..3cd63d20f11a390ccf8db01308272194c560734d 100644 (file)
@@ -5,6 +5,7 @@
 using System;
 using System.Collections.Generic;
 using System.Diagnostics.Tracing;
+using System.Text;
 using Microsoft.Diagnostics.NETCore.Client;
 using Microsoft.Extensions.Logging;
 
@@ -12,44 +13,111 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
 {
     public class LoggingSourceConfiguration : MonitoringSourceConfiguration
     {
-        private const string UseAppFilters = "UseAppFilters";
-
-        private readonly LogLevel _level;
-        private readonly bool _useAppFilters;
+        private readonly string _filterSpecs;
+        private readonly long _keywords;
+        private readonly EventLevel _level;
 
         /// <summary>
         /// Creates a new logging source configuration.
         /// </summary>
-        /// <param name="level">The logging level. Log messages at or above the log level will be included.</param>
-        /// <param name="useAppFilters">Use the UseAppFilters filterspec. This supersedes the log level and generates
-        /// log messages with the same levels per category as specified by the application configuration.</param>
-        public LoggingSourceConfiguration(LogLevel level = LogLevel.Debug, bool useAppFilters = false)
+        public LoggingSourceConfiguration(LogLevel level, LogMessageType messageType, IDictionary<string, LogLevel?> filterSpecs, bool useAppFilters)
         {
-            _level = level;
-            _useAppFilters = useAppFilters;
+            _filterSpecs = ToFilterSpecsString(filterSpecs, useAppFilters);
+            _keywords = (long)ToKeywords(messageType);
+            _level = ToEventLevel(level);
         }
 
         public override IList<EventPipeProvider> GetProviders()
         {
-            string filterSpec = _useAppFilters ? UseAppFilters : FormattableString.Invariant($"*:{_level:G}");
-
-            var providers = new List<EventPipeProvider>()
+            return new List<EventPipeProvider>()
             {
-
-                // Logging
                 new EventPipeProvider(
                     MicrosoftExtensionsLoggingProviderName,
-                    EventLevel.LogAlways,
-                    (long)(LoggingEventSource.Keywords.JsonMessage | LoggingEventSource.Keywords.FormattedMessage),
+                    _level,
+                    _keywords,
                     arguments: new Dictionary<string,string>
                         {
-
-                            { "FilterSpecs", filterSpec }
+                            { "FilterSpecs", _filterSpecs }
                         }
                 )
             };
+        }
+
+        private static string ToFilterSpecsString(IDictionary<string, LogLevel?> filterSpecs, bool useAppFilters)
+        {
+            if (!useAppFilters && (filterSpecs?.Count).GetValueOrDefault(0) == 0)
+            {
+                return String.Empty;
+            }
+
+            StringBuilder filterSpecsBuilder = new StringBuilder();
+
+            if (useAppFilters)
+            {
+                filterSpecsBuilder.Append("UseAppFilters");
+            }
+
+            if (null != filterSpecs)
+            {
+                foreach (KeyValuePair<string, LogLevel?> filterSpec in filterSpecs)
+                {
+                    if (!string.IsNullOrEmpty(filterSpec.Key))
+                    {
+                        if (filterSpecsBuilder.Length > 0)
+                        {
+                            filterSpecsBuilder.Append(";");
+                        }
+                        filterSpecsBuilder.Append(filterSpec.Key);
+                        if (filterSpec.Value.HasValue)
+                        {
+                            filterSpecsBuilder.Append(":");
+                            filterSpecsBuilder.Append(filterSpec.Value.Value.ToString("G"));
+                        }
+                    }
+                }
+            }
+
+            return filterSpecsBuilder.ToString();
+        }
 
-            return providers;
+        private static EventLevel ToEventLevel(LogLevel logLevel)
+        {
+            switch (logLevel)
+            {
+                case LogLevel.None:
+                    throw new NotSupportedException($"{nameof(LogLevel)} {nameof(LogLevel.None)} is not supported as the default log level.");
+                case LogLevel.Trace:
+                    return EventLevel.LogAlways;
+                case LogLevel.Debug:
+                    return EventLevel.Verbose;
+                case LogLevel.Information:
+                    return EventLevel.Informational;
+                case LogLevel.Warning:
+                    return EventLevel.Warning;
+                case LogLevel.Error:
+                    return EventLevel.Error;
+                case LogLevel.Critical:
+                    return EventLevel.Critical;
+            }
+            throw new InvalidOperationException($"Unable to convert {logLevel:G} to EventLevel.");
+        }
+
+        private static EventKeywords ToKeywords(LogMessageType messageType)
+        {
+            EventKeywords keywords = 0;
+            if (messageType.HasFlag(LogMessageType.FormattedMessage))
+            {
+                keywords |= LoggingEventSource.Keywords.FormattedMessage;
+            }
+            if (messageType.HasFlag(LogMessageType.JsonMessage))
+            {
+                keywords |= LoggingEventSource.Keywords.JsonMessage;
+            }
+            if (messageType.HasFlag(LogMessageType.Message))
+            {
+                keywords |= LoggingEventSource.Keywords.Message;
+            }
+            return keywords;
         }
 
         private sealed class LoggingEventSource
index 93b49b42afcec2d9ced59d48e21d31a9045893a7..31db6a531ba914fb3d30d9d3a134c1ead7b48ef6 100644 (file)
@@ -25,7 +25,11 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
 
         protected override MonitoringSourceConfiguration CreateConfiguration()
         {
-            return new LoggingSourceConfiguration(Settings.LogLevel, Settings.UseAppFilters);
+            return new LoggingSourceConfiguration(
+                Settings.LogLevel,
+                LogMessageType.FormattedMessage | LogMessageType.JsonMessage,
+                Settings.FilterSpecs,
+                Settings.UseAppFilters);
         }
 
         protected override Task OnEventSourceAvailable(EventPipeEventSource eventSource, Func<Task> stopSessionAsync, CancellationToken token)
index 4e510038dcc3c577ac6c68b042c6ee8371471fc9..cccaefdcf76cee2d7067b09dada8695e556d5188 100644 (file)
@@ -3,17 +3,19 @@
 // See the LICENSE file in the project root for more information.
 
 using Microsoft.Extensions.Logging;
-using System;
 using System.Collections.Generic;
-using System.Text;
 
 namespace Microsoft.Diagnostics.Monitoring.EventPipe
 {
     internal class EventLogsPipelineSettings : EventSourcePipelineSettings
     {
-        public LogLevel LogLevel { get; set; }
+        // The default log level for all categories
+        public LogLevel LogLevel { get; set; } = LogLevel.Trace;
 
-        //This setting will set the levels to application default.
-        public bool UseAppFilters { get; set; }
+        // The logger categories and levels at which log entries are collected.
+        public IDictionary<string, LogLevel?> FilterSpecs { get; set; }
+
+        // This setting will collect logs for the application-defined categories and levels.
+        public bool UseAppFilters { get; set; } = true;
     }
 }
index b85d1379adb6c4bd5f8ea22bd5c26c7cf15a51db..ebc86212bbc6658913bf2fe3c019ccb0247fad73 100644 (file)
@@ -1,4 +1,8 @@
-using Microsoft.Extensions.DependencyInjection;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Logging;
 using System;
 using System.Collections.Generic;
@@ -8,6 +12,8 @@ namespace EventPipeTracee
 {
     class Program
     {
+        private const string AppLoggerCategoryName = "AppLoggerCategory";
+
         static void Main(string[] args)
         {
             TestBody(args[0]);
@@ -22,10 +28,14 @@ namespace EventPipeTracee
             serviceCollection.AddLogging(builder =>
             {
                 builder.AddEventSourceLogger();
+                // Set application defined levels
+                builder.AddFilter(null, LogLevel.Error); // Default
+                builder.AddFilter(AppLoggerCategoryName, LogLevel.Warning);
             });
 
             using var loggerFactory = serviceCollection.BuildServiceProvider().GetService<ILoggerFactory>();
-            var logger = loggerFactory.CreateLogger(loggerCategory);
+            var customCategoryLogger = loggerFactory.CreateLogger(loggerCategory);
+            var appCategoryLogger = loggerFactory.CreateLogger(AppLoggerCategoryName);
 
             Console.Error.WriteLine($"{DateTime.UtcNow} Awaiting start");
             Console.Error.Flush();
@@ -36,7 +46,7 @@ namespace EventPipeTracee
 
             Console.Error.WriteLine($"{DateTime.UtcNow} Starting test body");
             Console.Error.Flush();
-            TestBodyCore(logger);
+            TestBodyCore(customCategoryLogger, appCategoryLogger);
 
             //Signal end of test data
             Console.WriteLine("1");
@@ -52,18 +62,22 @@ namespace EventPipeTracee
         }
 
         //TODO At some point we may want parameters to choose different test bodies.
-        private static void TestBodyCore(ILogger logger)
+        private static void TestBodyCore(ILogger customCategoryLogger, ILogger appCategoryLogger)
         {
             //Json data is always converted to strings for ActivityStart events.
-            using (var scope = logger.BeginScope(new Dictionary<string, object> {
+            using (var scope = customCategoryLogger.BeginScope(new Dictionary<string, object> {
                     { "IntValue", "5" },
                     { "BoolValue", "true" },
                     { "StringValue", "test" } }.ToList()))
             {
-                logger.LogWarning("Some warning message with {arg}", 6);
+                customCategoryLogger.LogInformation("Some warning message with {arg}", 6);
             }
 
-            logger.LogWarning("Another message");
+            customCategoryLogger.LogWarning("Another message");
+
+            appCategoryLogger.LogInformation("Information message.");
+            appCategoryLogger.LogWarning("Warning message.");
+            appCategoryLogger.LogError("Error message.");
         }
     }
 }
index 6bb7028100c4127f84046c99be629ff2fbace54c..afe0c304bad252d50098b8a79a5f66600f8c1f7f 100644 (file)
@@ -2,28 +2,26 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
+using Microsoft.Diagnostics.NETCore.Client;
+using Microsoft.Diagnostics.NETCore.Client.UnitTests;
+using Microsoft.Extensions.Logging;
 using System;
-using System.Collections;
 using System.Collections.Generic;
 using System.IO;
-using System.Runtime.InteropServices;
-using System.Text;
 using System.Text.Json;
 using System.Threading;
 using System.Threading.Tasks;
-using Microsoft.Diagnostics.Monitoring;
-using Microsoft.Diagnostics.Monitoring.EventPipe;
-using Microsoft.Diagnostics.NETCore.Client;
-using Microsoft.Diagnostics.NETCore.Client.UnitTests;
-using Microsoft.Extensions.Logging;
 using Xunit;
 using Xunit.Abstractions;
-using Xunit.Extensions;
 
 namespace Microsoft.Diagnostics.Monitoring.EventPipe.UnitTests
 {
     public class EventLogsPipelineUnitTests
     {
+        private const string WildcardCagetoryName = "*";
+        private const string AppLoggerCategoryName = "AppLoggerCategory";
+        private const string LoggerRemoteTestName = "LoggerRemoteTest";
+
         private readonly ITestOutputHelper _output;
 
         public EventLogsPipelineUnitTests(ITestOutputHelper output)
@@ -31,19 +29,181 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe.UnitTests
             _output = output;
         }
 
+        /// <summary>
+        /// Test that all log events are collected if no filters are specified.
+        /// </summary>
+        [Fact]
+        public async Task TestLogsAllCategoriesAllLevels()
+        {
+            using Stream outputStream = await GetLogsAsync(settings =>
+            {
+                settings.UseAppFilters = false;
+            });
+
+            Assert.True(outputStream.Length > 0, "No data written by logging process.");
+
+            using var reader = new StreamReader(outputStream);
+
+            ValidateLoggerRemoteCategoryInformationMessage(reader);
+            ValidateLoggerRemoteCategoryWarningMessage(reader);
+            ValidateAppLoggerCategoryInformationMessage(reader);
+            ValidateAppLoggerCategoryWarningMessage(reader);
+            ValidateAppLoggerCategoryErrorMessage(reader);
+
+            Assert.True(reader.EndOfStream, "Expected to have read all entries from stream.");
+        }
+
+        /// <summary>
+        /// Test that log events at or above the default level are collected.
+        /// </summary>
+        [Fact]
+        public async Task TestLogsAllCategoriesDefaultLevel()
+        {
+            using Stream outputStream = await GetLogsAsync(settings =>
+            {
+                settings.UseAppFilters = false;
+                settings.LogLevel = LogLevel.Warning;
+            });
+
+            Assert.True(outputStream.Length > 0, "No data written by logging process.");
+
+            using var reader = new StreamReader(outputStream);
+
+            ValidateLoggerRemoteCategoryWarningMessage(reader);
+            ValidateAppLoggerCategoryWarningMessage(reader);
+            ValidateAppLoggerCategoryErrorMessage(reader);
+
+            Assert.True(reader.EndOfStream, "Expected to have read all entries from stream.");
+        }
+
+        /// <summary>
+        /// Test that log events at the default level are collected for categories without a specified level.
+        /// </summary>
+        [Fact]
+        public async Task TestLogsAllCategoriesDefaultLevelFallback()
+        {
+            using Stream outputStream = await GetLogsAsync(settings =>
+            {
+                settings.UseAppFilters = false;
+                settings.LogLevel = LogLevel.Error;
+                settings.FilterSpecs = new Dictionary<string, LogLevel?>()
+                {
+                    { AppLoggerCategoryName, null },
+                    { LoggerRemoteTestName, LogLevel.Trace }
+                };
+            });
+
+            Assert.True(outputStream.Length > 0, "No data written by logging process.");
+
+            using var reader = new StreamReader(outputStream);
+
+            ValidateLoggerRemoteCategoryInformationMessage(reader);
+            ValidateLoggerRemoteCategoryWarningMessage(reader);
+            ValidateAppLoggerCategoryErrorMessage(reader);
+
+            Assert.True(reader.EndOfStream, "Expected to have read all entries from stream.");
+        }
+
+        /// <summary>
+        /// Test that LogLevel.None is not supported as the default log level.
+        /// </summary>
+        [Fact]
+        public Task TestLogsAllCategoriesDefaultLevelNoneNotSupported()
+        {
+            return Assert.ThrowsAsync<NotSupportedException>(() => GetLogsAsync(settings =>
+            {
+                settings.UseAppFilters = false;
+                settings.LogLevel = LogLevel.None;
+            }));
+        }
+
+        /// <summary>
+        /// Test that log events are collected for the categories and levels specified by the application.
+        /// </summary>
+        [Fact]
+        public async Task TestLogsUseAppFilters()
+        {
+            using Stream outputStream = await GetLogsAsync();
+
+            Assert.True(outputStream.Length > 0, "No data written by logging process.");
+
+            using var reader = new StreamReader(outputStream);
+
+            ValidateAppLoggerCategoryWarningMessage(reader);
+            ValidateAppLoggerCategoryErrorMessage(reader);
+
+            Assert.True(reader.EndOfStream, "Expected to have read all entries from stream.");
+        }
+
+        /// <summary>
+        /// Test that log events are collected for the categories and levels specified by the application
+        /// and for the categories and levels specified in the filter specs.
+        /// </summary>
         [Fact]
-        public async Task TestLogs()
+        public async Task TestLogsUseAppFiltersAndFilterSpecs()
+        {
+            using Stream outputStream = await GetLogsAsync(settings =>
+            {
+                settings.FilterSpecs = new Dictionary<string, LogLevel?>()
+                {
+                    { LoggerRemoteTestName, LogLevel.Warning }
+                };
+            });
+
+            Assert.True(outputStream.Length > 0, "No data written by logging process.");
+
+            using var reader = new StreamReader(outputStream);
+
+            ValidateLoggerRemoteCategoryWarningMessage(reader);
+            ValidateAppLoggerCategoryWarningMessage(reader);
+            ValidateAppLoggerCategoryErrorMessage(reader);
+
+            Assert.True(reader.EndOfStream, "Expected to have read all entries from stream.");
+        }
+
+        /// <summary>
+        /// Test that log events are collected for wildcard categories.
+        /// </summary>
+        [Fact]
+        public async Task TestLogsWildcardCategory()
+        {
+            using Stream outputStream = await GetLogsAsync(settings =>
+            {
+                settings.UseAppFilters = false;
+                settings.LogLevel = LogLevel.Critical;
+                settings.FilterSpecs = new Dictionary<string, LogLevel?>()
+                {
+                    { WildcardCagetoryName, LogLevel.Warning },
+                    { LoggerRemoteTestName, LogLevel.Error },
+                };
+            });
+
+            Assert.True(outputStream.Length > 0, "No data written by logging process.");
+
+            using var reader = new StreamReader(outputStream);
+
+            ValidateAppLoggerCategoryWarningMessage(reader);
+            ValidateAppLoggerCategoryErrorMessage(reader);
+
+            Assert.True(reader.EndOfStream, "Expected to have read all entries from stream.");
+        }
+
+        private async Task<Stream> GetLogsAsync(Action<EventLogsPipelineSettings> settingsCallback = null)
         {
             var outputStream = new MemoryStream();
 
-            await using (var testExecution = StartTraceeProcess("LoggerRemoteTest"))
+            await using (var testExecution = StartTraceeProcess(LoggerRemoteTestName))
             {
                 //TestRunner should account for start delay to make sure that the diagnostic pipe is available.
 
                 using var loggerFactory = new LoggerFactory(new[] { new TestStreamingLoggerProvider(outputStream) });
                 var client = new DiagnosticsClient(testExecution.TestRunner.Pid);
 
-                var logSettings = new EventLogsPipelineSettings { Duration = Timeout.InfiniteTimeSpan};
+                var logSettings = new EventLogsPipelineSettings { Duration = Timeout.InfiniteTimeSpan };
+                if (null != settingsCallback)
+                {
+                    settingsCallback(logSettings);
+                }
                 await using var pipeline = new EventLogsPipeline(client, logSettings, loggerFactory);
 
                 await PipelineTestUtilities.ExecutePipelineWithDebugee(pipeline, testExecution);
@@ -51,27 +211,31 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe.UnitTests
 
             outputStream.Position = 0L;
 
-            Assert.True(outputStream.Length > 0, "No data written by logging process.");
-
-            using var reader = new StreamReader(outputStream);
+            return outputStream;
+        }
 
-            string firstMessage = reader.ReadLine();
-            Assert.NotNull(firstMessage);
+        private static void ValidateLoggerRemoteCategoryInformationMessage(StreamReader reader)
+        {
+            string message = reader.ReadLine();
+            Assert.NotNull(message);
 
-            LoggerTestResult result = JsonSerializer.Deserialize<LoggerTestResult>(firstMessage);
+            LoggerTestResult result = JsonSerializer.Deserialize<LoggerTestResult>(message);
             Assert.Equal("Some warning message with 6", result.Message);
-            Assert.Equal("LoggerRemoteTest", result.Category);
-            Assert.Equal("Warning", result.LogLevel);
+            Assert.Equal(LoggerRemoteTestName, result.Category);
+            Assert.Equal("Information", result.LogLevel);
             Assert.Equal("0", result.EventId);
             Validate(result.Scopes, ("BoolValue", "true"), ("StringValue", "test"), ("IntValue", "5"));
             Validate(result.Arguments, ("arg", "6"));
+        }
 
-            string secondMessage = reader.ReadLine();
-            Assert.NotNull(secondMessage);
+        private static void ValidateLoggerRemoteCategoryWarningMessage(StreamReader reader)
+        {
+            string message = reader.ReadLine();
+            Assert.NotNull(message);
 
-            result = JsonSerializer.Deserialize<LoggerTestResult>(secondMessage);
+            LoggerTestResult result = JsonSerializer.Deserialize<LoggerTestResult>(message);
             Assert.Equal("Another message", result.Message);
-            Assert.Equal("LoggerRemoteTest", result.Category);
+            Assert.Equal(LoggerRemoteTestName, result.Category);
             Assert.Equal("Warning", result.LogLevel);
             Assert.Equal("0", result.EventId);
             Assert.Equal(0, result.Scopes.Count);
@@ -79,6 +243,51 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe.UnitTests
             Assert.Equal(1, result.Arguments.Count);
         }
 
+        private static void ValidateAppLoggerCategoryInformationMessage(StreamReader reader)
+        {
+            string message = reader.ReadLine();
+            Assert.NotNull(message);
+
+            LoggerTestResult result = JsonSerializer.Deserialize<LoggerTestResult>(message);
+            Assert.Equal("Information message.", result.Message);
+            Assert.Equal(AppLoggerCategoryName, result.Category);
+            Assert.Equal("Information", result.LogLevel);
+            Assert.Equal("0", result.EventId);
+            Assert.Equal(0, result.Scopes.Count);
+            //We are expecting only the original format
+            Assert.Equal(1, result.Arguments.Count);
+        }
+
+        private static void ValidateAppLoggerCategoryWarningMessage(StreamReader reader)
+        {
+            string message = reader.ReadLine();
+            Assert.NotNull(message);
+
+            LoggerTestResult result = JsonSerializer.Deserialize<LoggerTestResult>(message);
+            Assert.Equal("Warning message.", result.Message);
+            Assert.Equal(AppLoggerCategoryName, result.Category);
+            Assert.Equal("Warning", result.LogLevel);
+            Assert.Equal("0", result.EventId);
+            Assert.Equal(0, result.Scopes.Count);
+            //We are expecting only the original format
+            Assert.Equal(1, result.Arguments.Count);
+        }
+
+        private static void ValidateAppLoggerCategoryErrorMessage(StreamReader reader)
+        {
+            string message = reader.ReadLine();
+            Assert.NotNull(message);
+
+            LoggerTestResult result = JsonSerializer.Deserialize<LoggerTestResult>(message);
+            Assert.Equal("Error message.", result.Message);
+            Assert.Equal(AppLoggerCategoryName, result.Category);
+            Assert.Equal("Error", result.LogLevel);
+            Assert.Equal("0", result.EventId);
+            Assert.Equal(0, result.Scopes.Count);
+            //We are expecting only the original format
+            Assert.Equal(1, result.Arguments.Count);
+        }
+
         private static void Validate(IDictionary<string, JsonElement> values, params (string key, object value)[] expectedValues)
         {
             Assert.NotNull(values);
index b2c53b4da38ee5f32586856da170c0f0412dbc0b..8410ef25cd30733118eafcf3f1b7cad0ca543817 100644 (file)
@@ -8,7 +8,9 @@
     <ProjectReference Include="$(MSBuildThisFileDirectory)..\..\Microsoft.Diagnostics.Monitoring.EventPipe\Microsoft.Diagnostics.Monitoring.EventPipe.csproj" />
     <ProjectReference Include="$(MSBuildThisFileDirectory)..\..\Microsoft.Diagnostics.NETCore.Client\Microsoft.Diagnostics.NETCore.Client.csproj" />
     <ProjectReference Include="$(MSBuildThisFileDirectory)..\Microsoft.Diagnostics.NETCore.Client\Microsoft.Diagnostics.NETCore.Client.UnitTests.csproj" />
-    <ProjectReference Include="..\EventPipeTracee\EventPipeTracee.csproj" PrivateAssets="all" />
+    <ProjectReference Include="..\EventPipeTracee\EventPipeTracee.csproj" PrivateAssets="all">
+      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
+    </ProjectReference>
   </ItemGroup>
 
   <ItemGroup>
index b6068422ee496656fec6d43246c9df91ec40e687..a7ee91a2ff651fd576b838d244a4c1f28787f394 100644 (file)
@@ -9,7 +9,9 @@
     <ProjectReference Include="$(MSBuildThisFileDirectory)..\..\Microsoft.Diagnostics.Monitoring.EventPipe\Microsoft.Diagnostics.Monitoring.EventPipe.csproj" />
     <ProjectReference Include="$(MSBuildThisFileDirectory)..\..\Microsoft.Diagnostics.NETCore.Client\Microsoft.Diagnostics.NETCore.Client.csproj" />
     <ProjectReference Include="$(MSBuildThisFileDirectory)..\Microsoft.Diagnostics.NETCore.Client\Microsoft.Diagnostics.NETCore.Client.UnitTests.csproj" />
-    <ProjectReference Include="..\EventPipeTracee\EventPipeTracee.csproj" PrivateAssets="all" />
+    <ProjectReference Include="..\EventPipeTracee\EventPipeTracee.csproj" PrivateAssets="all">
+      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
+    </ProjectReference>
   </ItemGroup>
 
   <ItemGroup>