From 35fb5d23e14b964f3f2df002e3f83837708c89d7 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Thu, 25 Jan 2024 11:33:15 +1000 Subject: [PATCH 1/4] Collect current activity trace and span ids --- .../Serilog/Events/LogEvent.cs | 33 +++++++++++++++--- .../Extensions/Logging/SerilogLogger.cs | 34 +++++++++---------- .../Seq.Extensions.Logging.Tests.csproj | 3 ++ .../Extensions/Logging/SerilogLoggerTests.cs | 2 +- .../Support/Some.cs | 2 +- 5 files changed, 51 insertions(+), 23 deletions(-) diff --git a/src/Seq.Extensions.Logging/Serilog/Events/LogEvent.cs b/src/Seq.Extensions.Logging/Serilog/Events/LogEvent.cs index 2c07cba..ab3221d 100644 --- a/src/Seq.Extensions.Logging/Serilog/Events/LogEvent.cs +++ b/src/Seq.Extensions.Logging/Serilog/Events/LogEvent.cs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +using System.Diagnostics; using Microsoft.Extensions.Logging; namespace Serilog.Events; @@ -22,6 +23,8 @@ namespace Serilog.Events; class LogEvent { readonly Dictionary _properties; + ActivityTraceId _traceId; + ActivitySpanId _spanId; /// /// Construct a new . @@ -31,14 +34,24 @@ class LogEvent /// An exception associated with the event, or null. /// The message template describing the event. /// Properties associated with the event, including those presented in . - public LogEvent(DateTimeOffset timestamp, LogLevel level, Exception exception, MessageTemplate messageTemplate, IEnumerable properties) + /// The id of the trace that was active when the event was created, if any. + /// The id of the span that was active when the event was created, if any. + public LogEvent( + DateTimeOffset timestamp, + LogLevel level, + Exception exception, + MessageTemplate messageTemplate, + IEnumerable properties, + ActivityTraceId traceId, + ActivitySpanId spanId) { - if (messageTemplate == null) throw new ArgumentNullException(nameof(messageTemplate)); if (properties == null) throw new ArgumentNullException(nameof(properties)); + _traceId = traceId; + _spanId = spanId; Timestamp = timestamp; Level = level; Exception = exception; - MessageTemplate = messageTemplate; + MessageTemplate = messageTemplate ?? throw new ArgumentNullException(nameof(messageTemplate)); _properties = new Dictionary(); foreach (var p in properties) AddOrUpdateProperty(p); @@ -54,6 +67,18 @@ public LogEvent(DateTimeOffset timestamp, LogLevel level, Exception exception, M /// public LogLevel Level { get; } + /// + /// The id of the trace that was active when the event was created, if any. + /// + [CLSCompliant(false)] + public ActivityTraceId? TraceId => _traceId == default ? null : _traceId; + + /// + /// The id of the span that was active when the event was created, if any. + /// + [CLSCompliant(false)] + public ActivitySpanId? SpanId => _spanId == default ? null : _spanId; + /// /// The message template describing the event. /// @@ -122,4 +147,4 @@ public void RemovePropertyIfPresent(string propertyName) { _properties.Remove(propertyName); } -} \ No newline at end of file +} diff --git a/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLogger.cs b/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLogger.cs index 651bfaa..4b120f5 100644 --- a/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLogger.cs +++ b/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLogger.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.Diagnostics; using Microsoft.Extensions.Logging; using Serilog.Core; using Serilog.Events; @@ -15,7 +16,7 @@ class SerilogLogger : FrameworkLogger readonly SerilogLoggerProvider _provider; readonly Logger _logger; - static readonly MessageTemplateParser _messageTemplateParser = new MessageTemplateParser(); + static readonly MessageTemplateParser MessageTemplateParser = new(); public SerilogLogger( SerilogLoggerProvider provider, @@ -55,25 +56,22 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except var properties = new List(); - var structure = state as IEnumerable>; - if (structure != null) + if (state is IEnumerable> structure) { foreach (var property in structure) { - if (property.Key == SerilogLoggerProvider.OriginalFormatPropertyName && property.Value is string) + if (property is { Key: SerilogLoggerProvider.OriginalFormatPropertyName, Value: string }) { messageTemplate = (string)property.Value; } else if (property.Key.StartsWith("@")) { - LogEventProperty destructured; - if (logger.BindProperty(property.Key.Substring(1), property.Value, true, out destructured)) + if (logger.BindProperty(property.Key.Substring(1), property.Value, true, out var destructured)) properties.Add(destructured); } else { - LogEventProperty bound; - if (logger.BindProperty(property.Key, property.Value, false, out bound)) + if (logger.BindProperty(property.Key, property.Value, false, out var bound)) properties.Add(bound); } } @@ -84,8 +82,7 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except if (messageTemplate == null && !stateTypeInfo.IsGenericType) { messageTemplate = "{" + stateType.Name + ":l}"; - LogEventProperty stateTypeProperty; - if (logger.BindProperty(stateType.Name, AsLoggableValue(state, formatter), false, out stateTypeProperty)) + if (logger.BindProperty(stateType.Name, AsLoggableValue(state, formatter), false, out var stateTypeProperty)) properties.Add(stateTypeProperty); } } @@ -98,16 +95,17 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except propertyName = "State"; messageTemplate = "{State:l}"; } + // ReSharper disable once ConditionIsAlwaysTrueOrFalse else if (formatter != null) { propertyName = "Message"; messageTemplate = "{Message:l}"; } + // ReSharper disable once ConditionIsAlwaysTrueOrFalse if (propertyName != null) { - LogEventProperty property; - if (logger.BindProperty(propertyName, AsLoggableValue(state, formatter), false, out property)) + if (logger.BindProperty(propertyName, AsLoggableValue(state, formatter), false, out var property)) properties.Add(property); } } @@ -115,17 +113,19 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except if (eventId.Id != 0 || eventId.Name != null) properties.Add(CreateEventIdProperty(eventId)); - var parsedTemplate = _messageTemplateParser.Parse(messageTemplate ?? ""); - var evt = new LogEvent(DateTimeOffset.Now, logLevel, exception, parsedTemplate, properties); + // ReSharper disable once ConstantNullCoalescingCondition + var parsedTemplate = MessageTemplateParser.Parse(messageTemplate ?? ""); + var currentActivity = Activity.Current; + var evt = new LogEvent(DateTimeOffset.Now, logLevel, exception, parsedTemplate, properties, currentActivity?.TraceId ?? default, currentActivity?.SpanId ?? default); logger.Write(evt); } static object AsLoggableValue(TState state, Func formatter) { - object sobj = state; + object stateObject = state; if (formatter != null) - sobj = formatter(state, null); - return sobj; + stateObject = formatter(state, null); + return stateObject; } static LogEventProperty CreateEventIdProperty(EventId eventId) diff --git a/test/Seq.Extensions.Logging.Tests/Seq.Extensions.Logging.Tests.csproj b/test/Seq.Extensions.Logging.Tests/Seq.Extensions.Logging.Tests.csproj index 4fa4a1a..238fe34 100644 --- a/test/Seq.Extensions.Logging.Tests/Seq.Extensions.Logging.Tests.csproj +++ b/test/Seq.Extensions.Logging.Tests/Seq.Extensions.Logging.Tests.csproj @@ -5,6 +5,8 @@ ../../asset/seqext.snk true true + Tests + latest @@ -18,6 +20,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/test/Seq.Extensions.Logging.Tests/Serilog/Extensions/Logging/SerilogLoggerTests.cs b/test/Seq.Extensions.Logging.Tests/Serilog/Extensions/Logging/SerilogLoggerTests.cs index ef0eee8..c7a41e2 100644 --- a/test/Seq.Extensions.Logging.Tests/Serilog/Extensions/Logging/SerilogLoggerTests.cs +++ b/test/Seq.Extensions.Logging.Tests/Serilog/Extensions/Logging/SerilogLoggerTests.cs @@ -40,7 +40,7 @@ public void LogsWhenNullFilterGiven() var logger = t.Item1; var sink = t.Item2; - logger.Log(LogLevel.Information, 0, TestMessage, null, null); + logger.Log(LogLevel.Information, 0, TestMessage, null, null!); Assert.Single(sink.Writes); } diff --git a/test/Seq.Extensions.Logging.Tests/Support/Some.cs b/test/Seq.Extensions.Logging.Tests/Support/Some.cs index 0aa1077..c04101b 100644 --- a/test/Seq.Extensions.Logging.Tests/Support/Some.cs +++ b/test/Seq.Extensions.Logging.Tests/Support/Some.cs @@ -36,7 +36,7 @@ public static LogEvent LogEvent(LogLevel level, Exception exception, string mess { throw new XunitException("Template could not be bound."); } - return new LogEvent(DateTimeOffset.Now, level, exception, template, properties); + return new LogEvent(DateTimeOffset.Now, level, exception, template, properties, default, default); } public static LogEvent DebugEvent() From 86deef0e901e83fb8a78e1baa6839530ff182ffc Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Thu, 25 Jan 2024 11:40:38 +1000 Subject: [PATCH 2/4] A little code cleanup --- .../WebExample/Controllers/HomeController.cs | 2 +- .../Serilog/Events/LevelAlias.cs | 5 ---- .../Extensions/Logging/SerilogLogger.cs | 24 +++++++++-------- .../Parameters/PropertyValueConverter.cs | 2 +- .../Extensions/Logging/SerilogLoggerTests.cs | 26 +++++++++---------- 5 files changed, 28 insertions(+), 31 deletions(-) diff --git a/example/WebExample/Controllers/HomeController.cs b/example/WebExample/Controllers/HomeController.cs index 6b97e65..add4f54 100644 --- a/example/WebExample/Controllers/HomeController.cs +++ b/example/WebExample/Controllers/HomeController.cs @@ -6,7 +6,7 @@ namespace WebExample.Controllers; public class HomeController : Controller { - private readonly ILogger _logger; + readonly ILogger _logger; public HomeController(ILogger logger) { diff --git a/src/Seq.Extensions.Logging/Serilog/Events/LevelAlias.cs b/src/Seq.Extensions.Logging/Serilog/Events/LevelAlias.cs index 42bc095..3d77bf5 100644 --- a/src/Seq.Extensions.Logging/Serilog/Events/LevelAlias.cs +++ b/src/Seq.Extensions.Logging/Serilog/Events/LevelAlias.cs @@ -28,9 +28,4 @@ static class LevelAlias /// The least significant level of event. /// public const LogLevel Minimum = LogLevel.Trace; - - /// - /// The most significant level of event. - /// - public const LogLevel Maximum = LogLevel.Critical; } \ No newline at end of file diff --git a/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLogger.cs b/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLogger.cs index 4b120f5..9b62c3e 100644 --- a/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLogger.cs +++ b/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLogger.cs @@ -9,6 +9,11 @@ using System.Reflection; using Serilog.Parsing; +#nullable enable +// ReSharper disable ConditionIsAlwaysTrueOrFalse +// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract +// ReSharper disable ConstantNullCoalescingCondition + namespace Serilog.Extensions.Logging; class SerilogLogger : FrameworkLogger @@ -21,7 +26,7 @@ class SerilogLogger : FrameworkLogger public SerilogLogger( SerilogLoggerProvider provider, Logger logger, - string name = null) + string? name = null) { if (logger == null) throw new ArgumentNullException(nameof(logger)); _provider = provider ?? throw new ArgumentNullException(nameof(provider)); @@ -39,12 +44,12 @@ public bool IsEnabled(LogLevel logLevel) return _logger.IsEnabled(logLevel); } - public IDisposable BeginScope(TState state) + public IDisposable? BeginScope(TState state) where TState: notnull { return _provider.BeginScope(state); } - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { if (!_logger.IsEnabled(logLevel)) { @@ -52,7 +57,7 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except } var logger = _logger; - string messageTemplate = null; + string? messageTemplate = null; var properties = new List(); @@ -89,23 +94,21 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except if (messageTemplate == null) { - string propertyName = null; + string? propertyName = null; if (state != null) { propertyName = "State"; messageTemplate = "{State:l}"; } - // ReSharper disable once ConditionIsAlwaysTrueOrFalse else if (formatter != null) { propertyName = "Message"; messageTemplate = "{Message:l}"; } - // ReSharper disable once ConditionIsAlwaysTrueOrFalse if (propertyName != null) { - if (logger.BindProperty(propertyName, AsLoggableValue(state, formatter), false, out var property)) + if (logger.BindProperty(propertyName, AsLoggableValue(state, formatter!), false, out var property)) properties.Add(property); } } @@ -113,16 +116,15 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except if (eventId.Id != 0 || eventId.Name != null) properties.Add(CreateEventIdProperty(eventId)); - // ReSharper disable once ConstantNullCoalescingCondition var parsedTemplate = MessageTemplateParser.Parse(messageTemplate ?? ""); var currentActivity = Activity.Current; var evt = new LogEvent(DateTimeOffset.Now, logLevel, exception, parsedTemplate, properties, currentActivity?.TraceId ?? default, currentActivity?.SpanId ?? default); logger.Write(evt); } - static object AsLoggableValue(TState state, Func formatter) + static object? AsLoggableValue(TState state, Func formatter) { - object stateObject = state; + object? stateObject = state; if (formatter != null) stateObject = formatter(state, null); return stateObject; diff --git a/src/Seq.Extensions.Logging/Serilog/Parameters/PropertyValueConverter.cs b/src/Seq.Extensions.Logging/Serilog/Parameters/PropertyValueConverter.cs index f477eee..cc59350 100644 --- a/src/Seq.Extensions.Logging/Serilog/Parameters/PropertyValueConverter.cs +++ b/src/Seq.Extensions.Logging/Serilog/Parameters/PropertyValueConverter.cs @@ -196,7 +196,7 @@ LogEventPropertyValue CreatePropertyValue(object value, Destructuring destructur return new ScalarValue(value.ToString()); } - private LogEventPropertyValue Stringify(object value) + LogEventPropertyValue Stringify(object value) { var stringified = value.ToString(); var truncated = TruncateIfNecessary(stringified); diff --git a/test/Seq.Extensions.Logging.Tests/Serilog/Extensions/Logging/SerilogLoggerTests.cs b/test/Seq.Extensions.Logging.Tests/Serilog/Extensions/Logging/SerilogLoggerTests.cs index c7a41e2..73f8d14 100644 --- a/test/Seq.Extensions.Logging.Tests/Serilog/Extensions/Logging/SerilogLoggerTests.cs +++ b/test/Seq.Extensions.Logging.Tests/Serilog/Extensions/Logging/SerilogLoggerTests.cs @@ -17,10 +17,10 @@ namespace Tests.Serilog.Extensions.Logging { public class SerilogLoggerTest { - private const string Name = "test"; - private const string TestMessage = "This is a test"; + const string Name = "test"; + const string TestMessage = "This is a test"; - private Tuple SetUp(LogLevel logLevel) + Tuple SetUp(LogLevel logLevel) { var sink = new SerilogSink(); @@ -52,12 +52,12 @@ public void LogsCorrectLevel() var logger = t.Item1; var sink = t.Item2; - logger.Log(LogLevel.Trace, 0, TestMessage, null, null); - logger.Log(LogLevel.Debug, 0, TestMessage, null, null); - logger.Log(LogLevel.Information, 0, TestMessage, null, null); - logger.Log(LogLevel.Warning, 0, TestMessage, null, null); - logger.Log(LogLevel.Error, 0, TestMessage, null, null); - logger.Log(LogLevel.Critical, 0, TestMessage, null, null); + logger.Log(LogLevel.Trace, 0, TestMessage, null, null!); + logger.Log(LogLevel.Debug, 0, TestMessage, null, null!); + logger.Log(LogLevel.Information, 0, TestMessage, null, null!); + logger.Log(LogLevel.Warning, 0, TestMessage, null, null!); + logger.Log(LogLevel.Error, 0, TestMessage, null, null!); + logger.Log(LogLevel.Critical, 0, TestMessage, null, null!); Assert.Equal(6, sink.Writes.Count); Assert.Equal(LogLevel.Trace, sink.Writes[0].Level); @@ -111,7 +111,7 @@ public void LogsWhenEnabled(LogLevel minLevel, LogLevel logLevel, int expected) var logger = t.Item1; var sink = t.Item2; - logger.Log(logLevel, 0, TestMessage, null, null); + logger.Log(logLevel, 0, TestMessage, null, null!); Assert.Equal(expected, sink.Writes.Count); } @@ -354,7 +354,7 @@ public void NamedScopesAreCaptured() Assert.Equal("Inner", items[1]); } - private class FoodScope : IEnumerable> + class FoodScope : IEnumerable> { readonly string _name; @@ -374,7 +374,7 @@ IEnumerator IEnumerable.GetEnumerator() } } - private class LuckyScope : IEnumerable> + class LuckyScope : IEnumerable> { readonly int _luckyNumber; @@ -394,7 +394,7 @@ IEnumerator IEnumerable.GetEnumerator() } } - private class Person + class Person { public string FirstName { get; set; } public string LastName { get; set; } From 059b7efdcb4a4751d2052ff499c385edbf55b2e5 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Thu, 25 Jan 2024 11:52:11 +1000 Subject: [PATCH 3/4] Tests, cleanup --- example/WebExample/Program.cs | 3 +- seq-extensions-logging.sln.DotSettings | 1 + .../Serilog/Events/LogEvent.cs | 8 +- .../Extensions/Logging/SerilogLogger.cs | 2 +- .../Compact/CompactJsonFormatter.cs | 15 + .../Logging/ExceptionDataEnricherTests.cs | 65 +- .../Extensions/Logging/SerilogLoggerTests.cs | 604 +++++++++--------- .../Extensions/Logging/Support/SerilogSink.cs | 17 +- .../Sinks/BatchedConnectionStatusTests.cs | 175 +++-- .../Sinks/Seq/ControlledLevelSwitchTests.cs | 143 ++--- .../Sinks/Seq/SeqPayloadFormatterTests.cs | 33 +- .../Support/Some.cs | 87 +-- 12 files changed, 590 insertions(+), 563 deletions(-) diff --git a/example/WebExample/Program.cs b/example/WebExample/Program.cs index 9fddfc5..3466a03 100644 --- a/example/WebExample/Program.cs +++ b/example/WebExample/Program.cs @@ -4,8 +4,7 @@ builder.Services.AddControllersWithViews(); // Use the Seq logging configuration in appsettings.json -builder.Host.ConfigureLogging(loggingBuilder => - loggingBuilder.AddSeq()); +builder.Logging.AddSeq(); var app = builder.Build(); diff --git a/seq-extensions-logging.sln.DotSettings b/seq-extensions-logging.sln.DotSettings index a6f6477..6942af0 100644 --- a/seq-extensions-logging.sln.DotSettings +++ b/seq-extensions-logging.sln.DotSettings @@ -2,6 +2,7 @@ <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb"><ExtraRule Prefix="" Suffix="" Style="AaBb" /></Policy> True True + True True True True \ No newline at end of file diff --git a/src/Seq.Extensions.Logging/Serilog/Events/LogEvent.cs b/src/Seq.Extensions.Logging/Serilog/Events/LogEvent.cs index ab3221d..c65b50d 100644 --- a/src/Seq.Extensions.Logging/Serilog/Events/LogEvent.cs +++ b/src/Seq.Extensions.Logging/Serilog/Events/LogEvent.cs @@ -37,9 +37,9 @@ class LogEvent /// The id of the trace that was active when the event was created, if any. /// The id of the span that was active when the event was created, if any. public LogEvent( - DateTimeOffset timestamp, - LogLevel level, - Exception exception, + DateTimeOffset timestamp, + LogLevel level, + Exception exception, MessageTemplate messageTemplate, IEnumerable properties, ActivityTraceId traceId, @@ -70,13 +70,11 @@ public LogEvent( /// /// The id of the trace that was active when the event was created, if any. /// - [CLSCompliant(false)] public ActivityTraceId? TraceId => _traceId == default ? null : _traceId; /// /// The id of the span that was active when the event was created, if any. /// - [CLSCompliant(false)] public ActivitySpanId? SpanId => _spanId == default ? null : _spanId; /// diff --git a/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLogger.cs b/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLogger.cs index 9b62c3e..4405aee 100644 --- a/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLogger.cs +++ b/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLogger.cs @@ -44,7 +44,7 @@ public bool IsEnabled(LogLevel logLevel) return _logger.IsEnabled(logLevel); } - public IDisposable? BeginScope(TState state) where TState: notnull + public IDisposable? BeginScope(TState state) where TState : notnull { return _provider.BeginScope(state); } diff --git a/src/Seq.Extensions.Logging/Serilog/Formatting/Compact/CompactJsonFormatter.cs b/src/Seq.Extensions.Logging/Serilog/Formatting/Compact/CompactJsonFormatter.cs index c4bbbd3..26d7fd7 100644 --- a/src/Seq.Extensions.Logging/Serilog/Formatting/Compact/CompactJsonFormatter.cs +++ b/src/Seq.Extensions.Logging/Serilog/Formatting/Compact/CompactJsonFormatter.cs @@ -16,6 +16,7 @@ using Serilog.Formatting.Json; using Serilog.Parsing; using Microsoft.Extensions.Logging; +// ReSharper disable PossibleMultipleEnumeration namespace Serilog.Formatting.Compact; @@ -72,6 +73,20 @@ public static void FormatEvent(LogEvent logEvent, TextWriter output, JsonValueFo JsonValueFormatter.WriteQuotedJsonString(logEvent.Exception.ToString(), output); } + if (logEvent.TraceId != null) + { + output.Write(",\"@tr\":\""); + output.Write(logEvent.TraceId.Value.ToHexString()); + output.Write('\"'); + } + + if (logEvent.SpanId != null) + { + output.Write(",\"@sp\":\""); + output.Write(logEvent.SpanId.Value.ToHexString()); + output.Write('\"'); + } + foreach (var property in logEvent.Properties) { var name = property.Key; diff --git a/test/Seq.Extensions.Logging.Tests/Seq/Extensions/Logging/ExceptionDataEnricherTests.cs b/test/Seq.Extensions.Logging.Tests/Seq/Extensions/Logging/ExceptionDataEnricherTests.cs index 1fc7b81..0d1676b 100644 --- a/test/Seq.Extensions.Logging.Tests/Seq/Extensions/Logging/ExceptionDataEnricherTests.cs +++ b/test/Seq.Extensions.Logging.Tests/Seq/Extensions/Logging/ExceptionDataEnricherTests.cs @@ -5,46 +5,45 @@ using Tests.Support; using Xunit; -namespace Tests.Seq.Extensions.Logging +namespace Tests.Seq.Extensions.Logging; + +public class ExceptionDataEnricherTests { - public class ExceptionDataEnricherTests + [Fact] + public void WhenNoDataIsPresentNoPropertyIsAdded() { - [Fact] - public void WhenNoDataIsPresentNoPropertyIsAdded() - { - var enricher = new ExceptionDataEnricher(); - var exception = new Exception(); - var evt = Some.ErrorEvent(exception); + var enricher = new ExceptionDataEnricher(); + var exception = new Exception(); + var evt = Some.ErrorEvent(exception); - enricher.Enrich(evt, Some.PropertyFactory()); + enricher.Enrich(evt, Some.PropertyFactory()); - Assert.Equal(0, evt.Properties.Count); - } + Assert.Equal(0, evt.Properties.Count); + } - [Fact] - public void WhenDataIsPresentThePropertyIsAdded() + [Fact] + public void WhenDataIsPresentThePropertyIsAdded() + { + var enricher = new ExceptionDataEnricher(); + var exception = new Exception() { - var enricher = new ExceptionDataEnricher(); - var exception = new Exception() + Data = { - Data = - { - ["A"] = 42, - ["B"] = "Hello" - } - }; - var evt = Some.ErrorEvent(exception); + ["A"] = 42, + ["B"] = "Hello" + } + }; + var evt = Some.ErrorEvent(exception); - enricher.Enrich(evt, Some.PropertyFactory()); + enricher.Enrich(evt, Some.PropertyFactory()); - Assert.Equal(1, evt.Properties.Count); - var data = evt.Properties["ExceptionData"]; - var value = Assert.IsType(data); - Assert.Equal(2, value.Properties.Count); - var a = Assert.IsType(value.Properties.Single(p => p.Name == "A").Value); - Assert.Equal(42, a.Value); - var b = Assert.IsType(value.Properties.Single(p => p.Name == "B").Value); - Assert.Equal("Hello", b.Value); - } + Assert.Equal(1, evt.Properties.Count); + var data = evt.Properties["ExceptionData"]; + var value = Assert.IsType(data); + Assert.Equal(2, value.Properties.Count); + var a = Assert.IsType(value.Properties.Single(p => p.Name == "A").Value); + Assert.Equal(42, a.Value); + var b = Assert.IsType(value.Properties.Single(p => p.Name == "B").Value); + Assert.Equal("Hello", b.Value); } -} +} \ No newline at end of file diff --git a/test/Seq.Extensions.Logging.Tests/Serilog/Extensions/Logging/SerilogLoggerTests.cs b/test/Seq.Extensions.Logging.Tests/Serilog/Extensions/Logging/SerilogLoggerTests.cs index 73f8d14..1df84d1 100644 --- a/test/Seq.Extensions.Logging.Tests/Serilog/Extensions/Logging/SerilogLoggerTests.cs +++ b/test/Seq.Extensions.Logging.Tests/Serilog/Extensions/Logging/SerilogLoggerTests.cs @@ -6,398 +6,402 @@ using Serilog.Events; using Microsoft.Extensions.Logging; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using Xunit; using Serilog.Extensions.Logging; using Seq.Extensions.Logging; using Tests.Serilog.Extensions.Logging.Support; +using Tests.Support; -namespace Tests.Serilog.Extensions.Logging +namespace Tests.Serilog.Extensions.Logging; + +public class SerilogLoggerTests { - public class SerilogLoggerTest + static SerilogLoggerTests() { - const string Name = "test"; - const string TestMessage = "This is a test"; + // This is necessary to force activity id allocation on .NET Framework and early .NET Core versions. When this isn't + // done, log events end up carrying null trace and span ids (which is fine). + Activity.DefaultIdFormat = ActivityIdFormat.W3C; + Activity.ForceDefaultIdFormat = true; + } - Tuple SetUp(LogLevel logLevel) - { - var sink = new SerilogSink(); + const string Name = "test"; + const string TestMessage = "This is a test"; - var l = new global::Serilog.Core.Logger(new global::Serilog.Core.LoggingLevelSwitch(logLevel), sink); + static (SerilogLogger logger, SerilogSink sink) SetUp(LogLevel logLevel) + { + var sink = new SerilogSink(); - var provider = new SerilogLoggerProvider(l); - provider.SetScopeProvider(new LoggerExternalScopeProvider()); - var logger = (SerilogLogger)provider.CreateLogger(Name); + var l = new global::Serilog.Core.Logger(new global::Serilog.Core.LoggingLevelSwitch(logLevel), sink); - return new Tuple(logger, sink); - } + var provider = new SerilogLoggerProvider(l); + provider.SetScopeProvider(new LoggerExternalScopeProvider()); + var logger = (SerilogLogger)provider.CreateLogger(Name); - [Fact] - public void LogsWhenNullFilterGiven() - { - var t = SetUp(LogLevel.Trace); - var logger = t.Item1; - var sink = t.Item2; + return (logger, sink); + } - logger.Log(LogLevel.Information, 0, TestMessage, null, null!); + [Fact] + public void LogsWhenNullFilterGiven() + { + var (logger, sink) = SetUp(LogLevel.Trace); - Assert.Single(sink.Writes); - } + logger.Log(LogLevel.Information, 0, TestMessage, null, null!); - [Fact] - public void LogsCorrectLevel() - { - var t = SetUp(LogLevel.Trace); - var logger = t.Item1; - var sink = t.Item2; + Assert.Single(sink.Writes); + } - logger.Log(LogLevel.Trace, 0, TestMessage, null, null!); - logger.Log(LogLevel.Debug, 0, TestMessage, null, null!); - logger.Log(LogLevel.Information, 0, TestMessage, null, null!); - logger.Log(LogLevel.Warning, 0, TestMessage, null, null!); - logger.Log(LogLevel.Error, 0, TestMessage, null, null!); - logger.Log(LogLevel.Critical, 0, TestMessage, null, null!); - - Assert.Equal(6, sink.Writes.Count); - Assert.Equal(LogLevel.Trace, sink.Writes[0].Level); - Assert.Equal(LogLevel.Debug, sink.Writes[1].Level); - Assert.Equal(LogLevel.Information, sink.Writes[2].Level); - Assert.Equal(LogLevel.Warning, sink.Writes[3].Level); - Assert.Equal(LogLevel.Error, sink.Writes[4].Level); - Assert.Equal(LogLevel.Critical, sink.Writes[5].Level); - } + [Fact] + public void LogsCorrectLevel() + { + var (logger, sink) = SetUp(LogLevel.Trace); + + logger.Log(LogLevel.Trace, 0, TestMessage, null, null!); + logger.Log(LogLevel.Debug, 0, TestMessage, null, null!); + logger.Log(LogLevel.Information, 0, TestMessage, null, null!); + logger.Log(LogLevel.Warning, 0, TestMessage, null, null!); + logger.Log(LogLevel.Error, 0, TestMessage, null, null!); + logger.Log(LogLevel.Critical, 0, TestMessage, null, null!); + + Assert.Equal(6, sink.Writes.Count); + Assert.Equal(LogLevel.Trace, sink.Writes[0].Level); + Assert.Equal(LogLevel.Debug, sink.Writes[1].Level); + Assert.Equal(LogLevel.Information, sink.Writes[2].Level); + Assert.Equal(LogLevel.Warning, sink.Writes[3].Level); + Assert.Equal(LogLevel.Error, sink.Writes[4].Level); + Assert.Equal(LogLevel.Critical, sink.Writes[5].Level); + } - [Theory] - [InlineData(LogLevel.Trace, LogLevel.Trace, 1)] - [InlineData(LogLevel.Trace, LogLevel.Debug, 1)] - [InlineData(LogLevel.Trace, LogLevel.Information, 1)] - [InlineData(LogLevel.Trace, LogLevel.Warning, 1)] - [InlineData(LogLevel.Trace, LogLevel.Error, 1)] - [InlineData(LogLevel.Trace, LogLevel.Critical, 1)] - [InlineData(LogLevel.Debug, LogLevel.Trace, 0)] - [InlineData(LogLevel.Debug, LogLevel.Debug, 1)] - [InlineData(LogLevel.Debug, LogLevel.Information, 1)] - [InlineData(LogLevel.Debug, LogLevel.Warning, 1)] - [InlineData(LogLevel.Debug, LogLevel.Error, 1)] - [InlineData(LogLevel.Debug, LogLevel.Critical, 1)] - [InlineData(LogLevel.Information, LogLevel.Trace, 0)] - [InlineData(LogLevel.Information, LogLevel.Debug, 0)] - [InlineData(LogLevel.Information, LogLevel.Information, 1)] - [InlineData(LogLevel.Information, LogLevel.Warning, 1)] - [InlineData(LogLevel.Information, LogLevel.Error, 1)] - [InlineData(LogLevel.Information, LogLevel.Critical, 1)] - [InlineData(LogLevel.Warning, LogLevel.Trace, 0)] - [InlineData(LogLevel.Warning, LogLevel.Debug, 0)] - [InlineData(LogLevel.Warning, LogLevel.Information, 0)] - [InlineData(LogLevel.Warning, LogLevel.Warning, 1)] - [InlineData(LogLevel.Warning, LogLevel.Error, 1)] - [InlineData(LogLevel.Warning, LogLevel.Critical, 1)] - [InlineData(LogLevel.Error, LogLevel.Trace, 0)] - [InlineData(LogLevel.Error, LogLevel.Debug, 0)] - [InlineData(LogLevel.Error, LogLevel.Information, 0)] - [InlineData(LogLevel.Error, LogLevel.Warning, 0)] - [InlineData(LogLevel.Error, LogLevel.Error, 1)] - [InlineData(LogLevel.Error, LogLevel.Critical, 1)] - [InlineData(LogLevel.Critical, LogLevel.Trace, 0)] - [InlineData(LogLevel.Critical, LogLevel.Debug, 0)] - [InlineData(LogLevel.Critical, LogLevel.Information, 0)] - [InlineData(LogLevel.Critical, LogLevel.Warning, 0)] - [InlineData(LogLevel.Critical, LogLevel.Error, 0)] - [InlineData(LogLevel.Critical, LogLevel.Critical, 1)] - public void LogsWhenEnabled(LogLevel minLevel, LogLevel logLevel, int expected) - { - var t = SetUp(minLevel); - var logger = t.Item1; - var sink = t.Item2; + [Theory] + [InlineData(LogLevel.Trace, LogLevel.Trace, 1)] + [InlineData(LogLevel.Trace, LogLevel.Debug, 1)] + [InlineData(LogLevel.Trace, LogLevel.Information, 1)] + [InlineData(LogLevel.Trace, LogLevel.Warning, 1)] + [InlineData(LogLevel.Trace, LogLevel.Error, 1)] + [InlineData(LogLevel.Trace, LogLevel.Critical, 1)] + [InlineData(LogLevel.Debug, LogLevel.Trace, 0)] + [InlineData(LogLevel.Debug, LogLevel.Debug, 1)] + [InlineData(LogLevel.Debug, LogLevel.Information, 1)] + [InlineData(LogLevel.Debug, LogLevel.Warning, 1)] + [InlineData(LogLevel.Debug, LogLevel.Error, 1)] + [InlineData(LogLevel.Debug, LogLevel.Critical, 1)] + [InlineData(LogLevel.Information, LogLevel.Trace, 0)] + [InlineData(LogLevel.Information, LogLevel.Debug, 0)] + [InlineData(LogLevel.Information, LogLevel.Information, 1)] + [InlineData(LogLevel.Information, LogLevel.Warning, 1)] + [InlineData(LogLevel.Information, LogLevel.Error, 1)] + [InlineData(LogLevel.Information, LogLevel.Critical, 1)] + [InlineData(LogLevel.Warning, LogLevel.Trace, 0)] + [InlineData(LogLevel.Warning, LogLevel.Debug, 0)] + [InlineData(LogLevel.Warning, LogLevel.Information, 0)] + [InlineData(LogLevel.Warning, LogLevel.Warning, 1)] + [InlineData(LogLevel.Warning, LogLevel.Error, 1)] + [InlineData(LogLevel.Warning, LogLevel.Critical, 1)] + [InlineData(LogLevel.Error, LogLevel.Trace, 0)] + [InlineData(LogLevel.Error, LogLevel.Debug, 0)] + [InlineData(LogLevel.Error, LogLevel.Information, 0)] + [InlineData(LogLevel.Error, LogLevel.Warning, 0)] + [InlineData(LogLevel.Error, LogLevel.Error, 1)] + [InlineData(LogLevel.Error, LogLevel.Critical, 1)] + [InlineData(LogLevel.Critical, LogLevel.Trace, 0)] + [InlineData(LogLevel.Critical, LogLevel.Debug, 0)] + [InlineData(LogLevel.Critical, LogLevel.Information, 0)] + [InlineData(LogLevel.Critical, LogLevel.Warning, 0)] + [InlineData(LogLevel.Critical, LogLevel.Error, 0)] + [InlineData(LogLevel.Critical, LogLevel.Critical, 1)] + public void LogsWhenEnabled(LogLevel minLevel, LogLevel logLevel, int expected) + { + var (logger, sink) = SetUp(minLevel); - logger.Log(logLevel, 0, TestMessage, null, null!); + logger.Log(logLevel, 0, TestMessage, null, null!); - Assert.Equal(expected, sink.Writes.Count); - } + Assert.Equal(expected, sink.Writes.Count); + } - [Fact] - public void LogsCorrectMessage() - { - var t = SetUp(LogLevel.Trace); - var logger = t.Item1; - var sink = t.Item2; + [Fact] + public void LogsCorrectMessage() + { + var (logger, sink) = SetUp(LogLevel.Trace); - logger.Log(LogLevel.Information, 0, null, null, null); - logger.Log(LogLevel.Information, 0, TestMessage, null, null); - logger.Log(LogLevel.Information, 0, null, null, (_, __) => TestMessage); + logger.Log(LogLevel.Information, 0, null, null, null!); + logger.Log(LogLevel.Information, 0, TestMessage, null, null!); + logger.Log(LogLevel.Information, 0, null, null, (_, _) => TestMessage); - Assert.Equal(3, sink.Writes.Count); + Assert.Equal(3, sink.Writes.Count); - Assert.Equal(1, sink.Writes[0].Properties.Count); - Assert.Empty(sink.Writes[0].RenderMessage()); + Assert.Equal(1, sink.Writes[0].Properties.Count); + Assert.Empty(sink.Writes[0].RenderMessage()); - Assert.Equal(2, sink.Writes[1].Properties.Count); - Assert.True(sink.Writes[1].Properties.ContainsKey("State")); - Assert.Equal(TestMessage, sink.Writes[1].RenderMessage()); + Assert.Equal(2, sink.Writes[1].Properties.Count); + Assert.True(sink.Writes[1].Properties.ContainsKey("State")); + Assert.Equal(TestMessage, sink.Writes[1].RenderMessage()); - Assert.Equal(2, sink.Writes[2].Properties.Count); - Assert.True(sink.Writes[2].Properties.ContainsKey("Message")); - Assert.Equal(TestMessage, sink.Writes[2].RenderMessage()); - } + Assert.Equal(2, sink.Writes[2].Properties.Count); + Assert.True(sink.Writes[2].Properties.ContainsKey("Message")); + Assert.Equal(TestMessage, sink.Writes[2].RenderMessage()); + } - [Fact] - public void CarriesException() - { - var t = SetUp(LogLevel.Trace); - var logger = t.Item1; - var sink = t.Item2; + [Fact] + public void CarriesException() + { + var (logger, sink) = SetUp(LogLevel.Trace); - var exception = new Exception(); + var exception = new Exception(); - logger.Log(LogLevel.Information, 0, "Test", exception, null); + logger.Log(LogLevel.Information, 0, "Test", exception, null!); - Assert.Single(sink.Writes); - Assert.Same(exception, sink.Writes[0].Exception); - } + Assert.Single(sink.Writes); + Assert.Same(exception, sink.Writes[0].Exception); + } - [Fact] - public void SingleScopeProperty() + [Fact] + public void SingleScopeProperty() + { + var (logger, sink) = SetUp(LogLevel.Trace); + + using (logger.BeginScope(new FoodScope("pizza"))) { - var t = SetUp(LogLevel.Trace); - var logger = t.Item1; - var sink = t.Item2; + logger.Log(LogLevel.Information, 0, TestMessage, null, null!); + } - using (logger.BeginScope(new FoodScope("pizza"))) - { - logger.Log(LogLevel.Information, 0, TestMessage, null, null); - } + Assert.Single(sink.Writes); + Assert.True(sink.Writes[0].Properties.ContainsKey("Name")); + Assert.Equal("\"pizza\"", sink.Writes[0].Properties["Name"].ToString()); + } - Assert.Single(sink.Writes); - Assert.True(sink.Writes[0].Properties.ContainsKey("Name")); - Assert.Equal("\"pizza\"", sink.Writes[0].Properties["Name"].ToString()); - } + [Fact] + public void NestedScopeSameProperty() + { + var (logger, sink) = SetUp(LogLevel.Trace); - [Fact] - public void NestedScopeSameProperty() + using (logger.BeginScope(new FoodScope("avocado"))) { - var t = SetUp(LogLevel.Trace); - var logger = t.Item1; - var sink = t.Item2; - - using (logger.BeginScope(new FoodScope("avocado"))) + using (logger.BeginScope(new FoodScope("bacon"))) { - using (logger.BeginScope(new FoodScope("bacon"))) - { - logger.Log(LogLevel.Information, 0, TestMessage, null, null); - } + logger.Log(LogLevel.Information, 0, TestMessage, null, null!); } - - // Should retain the property of the most specific scope - Assert.Single(sink.Writes); - Assert.True(sink.Writes[0].Properties.ContainsKey("Name")); - Assert.Equal("\"bacon\"", sink.Writes[0].Properties["Name"].ToString()); } - [Fact] - public void NestedScopesDifferentProperties() - { - var t = SetUp(LogLevel.Trace); - var logger = t.Item1; - var sink = t.Item2; + // Should retain the property of the most specific scope + Assert.Single(sink.Writes); + Assert.True(sink.Writes[0].Properties.ContainsKey("Name")); + Assert.Equal("\"bacon\"", sink.Writes[0].Properties["Name"].ToString()); + } + + [Fact] + public void NestedScopesDifferentProperties() + { + var (logger, sink) = SetUp(LogLevel.Trace); - using (logger.BeginScope(new FoodScope("spaghetti"))) + using (logger.BeginScope(new FoodScope("spaghetti"))) + { + using (logger.BeginScope(new LuckyScope(7))) { - using (logger.BeginScope(new LuckyScope(7))) - { - logger.Log(LogLevel.Information, 0, TestMessage, null, null); - } + logger.Log(LogLevel.Information, 0, TestMessage, null, null!); } - - Assert.Single(sink.Writes); - Assert.True(sink.Writes[0].Properties.ContainsKey("Name")); - Assert.Equal("\"spaghetti\"", sink.Writes[0].Properties["Name"].ToString()); - Assert.True(sink.Writes[0].Properties.ContainsKey("LuckyNumber")); - Assert.Equal("7", sink.Writes[0].Properties["LuckyNumber"].ToString()); } - [Fact] - public void CarriesMessageTemplateProperties() - { - var selfLog = new StringWriter(); - SelfLog.Enable(selfLog); + Assert.Single(sink.Writes); + Assert.True(sink.Writes[0].Properties.ContainsKey("Name")); + Assert.Equal("\"spaghetti\"", sink.Writes[0].Properties["Name"].ToString()); + Assert.True(sink.Writes[0].Properties.ContainsKey("LuckyNumber")); + Assert.Equal("7", sink.Writes[0].Properties["LuckyNumber"].ToString()); + } - var t = SetUp(LogLevel.Trace); - var logger = t.Item1; - var sink = t.Item2; + [Fact] + public void CarriesMessageTemplateProperties() + { + var selfLog = new StringWriter(); + SelfLog.Enable(selfLog); - logger.LogInformation("Hello, {Recipient}", "World"); + var (logger, sink) = SetUp(LogLevel.Trace); - Assert.True(sink.Writes[0].Properties.ContainsKey("Recipient")); - Assert.Equal("\"World\"", sink.Writes[0].Properties["Recipient"].ToString()); - Assert.Equal("Hello, {Recipient}", sink.Writes[0].MessageTemplate.Text); + logger.LogInformation("Hello, {Recipient}", "World"); - SelfLog.Disable(); - Assert.Empty(selfLog.ToString()); - } + Assert.True(sink.Writes[0].Properties.ContainsKey("Recipient")); + Assert.Equal("\"World\"", sink.Writes[0].Properties["Recipient"].ToString()); + Assert.Equal("Hello, {Recipient}", sink.Writes[0].MessageTemplate.Text); - [Fact] - public void CarriesEventIdIfNonzero() - { - var t = SetUp(LogLevel.Trace); - var logger = t.Item1; - var sink = t.Item2; + SelfLog.Disable(); + Assert.Empty(selfLog.ToString()); + } - int expected = 42; + [Fact] + public void CarriesEventIdIfNonzero() + { + var (logger, sink) = SetUp(LogLevel.Trace); - logger.Log(LogLevel.Information, expected, "Test", null, null); + const int expected = 42; - Assert.Single(sink.Writes); + logger.Log(LogLevel.Information, expected, "Test", null, null!); - var eventId = (StructureValue)sink.Writes[0].Properties["EventId"]; - var id = (ScalarValue)eventId.Properties.Single(p => p.Name == "Id").Value; - Assert.Equal(42, id.Value); - } + Assert.Single(sink.Writes); - [Fact] - public void BeginScopeDestructuresObjectsWhenDestructurerIsUsedInMessageTemplate() + var eventId = (StructureValue)sink.Writes[0].Properties["EventId"]; + var id = (ScalarValue)eventId.Properties.Single(p => p.Name == "Id").Value; + Assert.Equal(42, id.Value); + } + + [Fact] + public void BeginScopeDestructuresObjectsWhenDestructurerIsUsedInMessageTemplate() + { + var (logger, sink) = SetUp(LogLevel.Trace); + + using (logger.BeginScope("{@Person}", new Person { FirstName = "John", LastName = "Smith" })) { - var t = SetUp(LogLevel.Trace); - var logger = t.Item1; - var sink = t.Item2; + logger.Log(LogLevel.Information, 0, TestMessage, null, null!); + } - using (logger.BeginScope("{@Person}", new Person { FirstName = "John", LastName = "Smith" })) - { - logger.Log(LogLevel.Information, 0, TestMessage, null, null); - } + Assert.Single(sink.Writes); + Assert.True(sink.Writes[0].Properties.ContainsKey("Person")); - Assert.Single(sink.Writes); - Assert.True(sink.Writes[0].Properties.ContainsKey("Person")); + var person = (StructureValue)sink.Writes[0].Properties["Person"]; + var firstName = (ScalarValue)person.Properties.Single(p => p.Name == "FirstName").Value; + var lastName = (ScalarValue)person.Properties.Single(p => p.Name == "LastName").Value; + Assert.Equal("John", firstName.Value); + Assert.Equal("Smith", lastName.Value); + } - var person = (StructureValue)sink.Writes[0].Properties["Person"]; - var firstName = (ScalarValue)person.Properties.Single(p => p.Name == "FirstName").Value; - var lastName = (ScalarValue)person.Properties.Single(p => p.Name == "LastName").Value; - Assert.Equal("John", firstName.Value); - Assert.Equal("Smith", lastName.Value); - } + [Fact] + public void BeginScopeDestructuresObjectsWhenDestructurerIsUsedInDictionary() + { + var (logger, sink) = SetUp(LogLevel.Trace); - [Fact] - public void BeginScopeDestructuresObjectsWhenDestructurerIsUsedInDictionary() + using (logger.BeginScope(new Dictionary { { "@Person", new Person { FirstName = "John", LastName = "Smith" } } })) { - var t = SetUp(LogLevel.Trace); - var logger = t.Item1; - var sink = t.Item2; + logger.Log(LogLevel.Information, 0, TestMessage, null, null!); + } - using (logger.BeginScope(new Dictionary { { "@Person", new Person { FirstName = "John", LastName = "Smith" } } })) - { - logger.Log(LogLevel.Information, 0, TestMessage, null, null); - } + Assert.Single(sink.Writes); + Assert.True(sink.Writes[0].Properties.ContainsKey("Person")); - Assert.Single(sink.Writes); - Assert.True(sink.Writes[0].Properties.ContainsKey("Person")); + var person = (StructureValue)sink.Writes[0].Properties["Person"]; + var firstName = (ScalarValue)person.Properties.Single(p => p.Name == "FirstName").Value; + var lastName = (ScalarValue)person.Properties.Single(p => p.Name == "LastName").Value; + Assert.Equal("John", firstName.Value); + Assert.Equal("Smith", lastName.Value); + } - var person = (StructureValue)sink.Writes[0].Properties["Person"]; - var firstName = (ScalarValue)person.Properties.Single(p => p.Name == "FirstName").Value; - var lastName = (ScalarValue)person.Properties.Single(p => p.Name == "LastName").Value; - Assert.Equal("John", firstName.Value); - Assert.Equal("Smith", lastName.Value); - } + [Fact] + public void BeginScopeDoesNotModifyKeyWhenDestructurerIsNotUsedInMessageTemplate() + { + var (logger, sink) = SetUp(LogLevel.Trace); - [Fact] - public void BeginScopeDoesNotModifyKeyWhenDestructurerIsNotUsedInMessageTemplate() + using (logger.BeginScope("{FirstName}", "John")) { - var t = SetUp(LogLevel.Trace); - var logger = t.Item1; - var sink = t.Item2; + logger.Log(LogLevel.Information, 0, TestMessage, null, null!); + } - using (logger.BeginScope("{FirstName}", "John")) - { - logger.Log(LogLevel.Information, 0, TestMessage, null, null); - } + Assert.Single(sink.Writes); + Assert.True(sink.Writes[0].Properties.ContainsKey("FirstName")); + } - Assert.Single(sink.Writes); - Assert.True(sink.Writes[0].Properties.ContainsKey("FirstName")); - } + [Fact] + public void BeginScopeDoesNotModifyKeyWhenDestructurerIsNotUsedInDictionary() + { + var (logger, sink) = SetUp(LogLevel.Trace); - [Fact] - public void BeginScopeDoesNotModifyKeyWhenDestructurerIsNotUsedInDictionary() + using (logger.BeginScope(new Dictionary { { "FirstName", "John" } })) { - var t = SetUp(LogLevel.Trace); - var logger = t.Item1; - var sink = t.Item2; + logger.Log(LogLevel.Information, 0, TestMessage, null, null!); + } - using (logger.BeginScope(new Dictionary { { "FirstName", "John" } })) - { - logger.Log(LogLevel.Information, 0, TestMessage, null, null); - } + Assert.Single(sink.Writes); + Assert.True(sink.Writes[0].Properties.ContainsKey("FirstName")); + } - Assert.Single(sink.Writes); - Assert.True(sink.Writes[0].Properties.ContainsKey("FirstName")); - } + [Fact] + public void NamedScopesAreCaptured() + { + var (logger, sink) = SetUp(LogLevel.Trace); - [Fact] - public void NamedScopesAreCaptured() + using (logger.BeginScope("Outer")) + using (logger.BeginScope("Inner")) { - var t = SetUp(LogLevel.Trace); - var logger = t.Item1; - var sink = t.Item2; + logger.Log(LogLevel.Information, 0, TestMessage, null, null!); + } - using (logger.BeginScope("Outer")) - using (logger.BeginScope("Inner")) - { - logger.Log(LogLevel.Information, 0, TestMessage, null, null); - } + Assert.Single(sink.Writes); - Assert.Single(sink.Writes); + Assert.True(sink.Writes[0].Properties.TryGetValue(SerilogLoggerProvider.ScopePropertyName, out var scopeValue)); - LogEventPropertyValue scopeValue; - Assert.True(sink.Writes[0].Properties.TryGetValue(SerilogLoggerProvider.ScopePropertyName, out scopeValue)); + var items = (scopeValue as SequenceValue)?.Elements.Select(e => ((ScalarValue)e).Value).Cast().ToArray(); + Assert.Equal(2, items!.Length); + Assert.Equal("Outer", items[0]); + Assert.Equal("Inner", items[1]); + } - var items = (scopeValue as SequenceValue)?.Elements.Select(e => ((ScalarValue)e).Value).Cast().ToArray(); - Assert.Equal(2, items.Length); - Assert.Equal("Outer", items[0]); - Assert.Equal("Inner", items[1]); - } + [Fact] + public void CurrentActivityIsCapturedAtLogEventCreation() + { + using var listener = new ActivityListener(); + listener.ShouldListenTo = _ => true; + listener.SampleUsingParentId = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllData; + listener.Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllData; + ActivitySource.AddActivityListener(listener); - class FoodScope : IEnumerable> - { - readonly string _name; + using var source = new ActivitySource(Some.String()); + using var activity = source.StartActivity(Some.String()); + Assert.NotNull(activity); + Assert.NotEqual(default(ActivityTraceId).ToHexString(), activity.TraceId.ToHexString()); + Assert.NotEqual(default(ActivitySpanId).ToHexString(), activity.SpanId.ToHexString()); - public FoodScope(string name) - { - _name = name; - } + var (logger, sink) = SetUp(LogLevel.Trace); + logger.Log(LogLevel.Information, 0, TestMessage, null, null!); - public IEnumerator> GetEnumerator() - { - yield return new KeyValuePair("Name", _name); - } + var single = sink.SingleWrite; - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } + Assert.Equal(activity.TraceId, single.TraceId); + Assert.Equal(activity.SpanId, single.SpanId); + } + + class FoodScope : IEnumerable> + { + readonly string _name; + + public FoodScope(string name) + { + _name = name; } - class LuckyScope : IEnumerable> + public IEnumerator> GetEnumerator() { - readonly int _luckyNumber; + yield return new KeyValuePair("Name", _name); + } - public LuckyScope(int luckyNumber) - { - _luckyNumber = luckyNumber; - } + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } - public IEnumerator> GetEnumerator() - { - yield return new KeyValuePair("LuckyNumber", _luckyNumber); - } + class LuckyScope : IEnumerable> + { + readonly int _luckyNumber; - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } + public LuckyScope(int luckyNumber) + { + _luckyNumber = luckyNumber; + } + + public IEnumerator> GetEnumerator() + { + yield return new KeyValuePair("LuckyNumber", _luckyNumber); } - class Person + IEnumerator IEnumerable.GetEnumerator() { - public string FirstName { get; set; } - public string LastName { get; set; } + return GetEnumerator(); } } + + class Person + { + // ReSharper disable once UnusedAutoPropertyAccessor.Local + public string FirstName { get; set; } + // ReSharper disable once UnusedAutoPropertyAccessor.Local + public string LastName { get; set; } + } } \ No newline at end of file diff --git a/test/Seq.Extensions.Logging.Tests/Serilog/Extensions/Logging/Support/SerilogSink.cs b/test/Seq.Extensions.Logging.Tests/Serilog/Extensions/Logging/Support/SerilogSink.cs index 2805972..baa8cb8 100644 --- a/test/Seq.Extensions.Logging.Tests/Serilog/Extensions/Logging/Support/SerilogSink.cs +++ b/test/Seq.Extensions.Logging.Tests/Serilog/Extensions/Logging/Support/SerilogSink.cs @@ -4,16 +4,17 @@ using System.Collections.Generic; using Serilog.Core; using Serilog.Events; +using Xunit; -namespace Tests.Serilog.Extensions.Logging.Support +namespace Tests.Serilog.Extensions.Logging.Support; + +class SerilogSink : ILogEventSink { - class SerilogSink : ILogEventSink - { - public List Writes { get; set; } = new List(); + public List Writes { get; } = []; + public LogEvent SingleWrite => Assert.Single(Writes); - public void Emit(LogEvent logEvent) - { - Writes.Add(logEvent); - } + public void Emit(LogEvent logEvent) + { + Writes.Add(logEvent); } } \ No newline at end of file diff --git a/test/Seq.Extensions.Logging.Tests/Serilog/Sinks/BatchedConnectionStatusTests.cs b/test/Seq.Extensions.Logging.Tests/Serilog/Sinks/BatchedConnectionStatusTests.cs index 49a7bcf..366dd4d 100644 --- a/test/Seq.Extensions.Logging.Tests/Serilog/Sinks/BatchedConnectionStatusTests.cs +++ b/test/Seq.Extensions.Logging.Tests/Serilog/Sinks/BatchedConnectionStatusTests.cs @@ -17,114 +17,113 @@ using Xunit; using Serilog.Sinks.PeriodicBatching; -namespace Seq.Extensions.Logging.Tests.Serilog.Sinks +namespace Seq.Extensions.Logging.Tests.Serilog.Sinks; + +public class BatchedConnectionStatusTests { - public class BatchedConnectionStatusTests + readonly TimeSpan DefaultPeriod = TimeSpan.FromSeconds(2); + + [Fact] + public void WhenNoFailuresHaveOccurredTheRegularIntervalIsUsed() { - readonly TimeSpan DefaultPeriod = TimeSpan.FromSeconds(2); + var bcs = new BatchedConnectionStatus(DefaultPeriod); + Assert.Equal(DefaultPeriod, bcs.NextInterval); + } - [Fact] - public void WhenNoFailuresHaveOccurredTheRegularIntervalIsUsed() - { - var bcs = new BatchedConnectionStatus(DefaultPeriod); - Assert.Equal(DefaultPeriod, bcs.NextInterval); - } + [Fact] + public void WhenOneFailureHasOccurredTheRegularIntervalIsUsed() + { + var bcs = new BatchedConnectionStatus(DefaultPeriod); + bcs.MarkFailure(); + Assert.Equal(DefaultPeriod, bcs.NextInterval); + } - [Fact] - public void WhenOneFailureHasOccurredTheRegularIntervalIsUsed() - { - var bcs = new BatchedConnectionStatus(DefaultPeriod); - bcs.MarkFailure(); - Assert.Equal(DefaultPeriod, bcs.NextInterval); - } + [Fact] + public void WhenTwoFailuresHaveOccurredTheIntervalBacksOff() + { + var bcs = new BatchedConnectionStatus(DefaultPeriod); + bcs.MarkFailure(); + bcs.MarkFailure(); + Assert.Equal(TimeSpan.FromSeconds(10), bcs.NextInterval); + } - [Fact] - public void WhenTwoFailuresHaveOccurredTheIntervalBacksOff() - { - var bcs = new BatchedConnectionStatus(DefaultPeriod); - bcs.MarkFailure(); - bcs.MarkFailure(); - Assert.Equal(TimeSpan.FromSeconds(10), bcs.NextInterval); - } + [Fact] + public void WhenABatchSucceedsTheStatusResets() + { + var bcs = new BatchedConnectionStatus(DefaultPeriod); + bcs.MarkFailure(); + bcs.MarkFailure(); + bcs.MarkSuccess(); + Assert.Equal(DefaultPeriod, bcs.NextInterval); + } - [Fact] - public void WhenABatchSucceedsTheStatusResets() - { - var bcs = new BatchedConnectionStatus(DefaultPeriod); - bcs.MarkFailure(); - bcs.MarkFailure(); - bcs.MarkSuccess(); - Assert.Equal(DefaultPeriod, bcs.NextInterval); - } + [Fact] + public void WhenThreeFailuresHaveOccurredTheIntervalBacksOff() + { + var bcs = new BatchedConnectionStatus(DefaultPeriod); + bcs.MarkFailure(); + bcs.MarkFailure(); + bcs.MarkFailure(); + Assert.Equal(TimeSpan.FromSeconds(20), bcs.NextInterval); + Assert.False(bcs.ShouldDropBatch); + } - [Fact] - public void WhenThreeFailuresHaveOccurredTheIntervalBacksOff() + [Fact] + public void When8FailuresHaveOccurredTheIntervalBacksOffAndBatchIsDropped() + { + var bcs = new BatchedConnectionStatus(DefaultPeriod); + for (var i = 0; i < 8; ++i) { - var bcs = new BatchedConnectionStatus(DefaultPeriod); - bcs.MarkFailure(); - bcs.MarkFailure(); - bcs.MarkFailure(); - Assert.Equal(TimeSpan.FromSeconds(20), bcs.NextInterval); Assert.False(bcs.ShouldDropBatch); + bcs.MarkFailure(); } + Assert.Equal(TimeSpan.FromMinutes(10), bcs.NextInterval); + Assert.True(bcs.ShouldDropBatch); + Assert.False(bcs.ShouldDropQueue); + } - [Fact] - public void When8FailuresHaveOccurredTheIntervalBacksOffAndBatchIsDropped() + [Fact] + public void When10FailuresHaveOccurredTheQueueIsDropped() + { + var bcs = new BatchedConnectionStatus(DefaultPeriod); + for (var i = 0; i < 10; ++i) { - var bcs = new BatchedConnectionStatus(DefaultPeriod); - for (var i = 0; i < 8; ++i) - { - Assert.False(bcs.ShouldDropBatch); - bcs.MarkFailure(); - } - Assert.Equal(TimeSpan.FromMinutes(10), bcs.NextInterval); - Assert.True(bcs.ShouldDropBatch); Assert.False(bcs.ShouldDropQueue); + bcs.MarkFailure(); } + Assert.True(bcs.ShouldDropQueue); + } - [Fact] - public void When10FailuresHaveOccurredTheQueueIsDropped() - { - var bcs = new BatchedConnectionStatus(DefaultPeriod); - for (var i = 0; i < 10; ++i) - { - Assert.False(bcs.ShouldDropQueue); - bcs.MarkFailure(); - } - Assert.True(bcs.ShouldDropQueue); - } - - [Fact] - public void AtTheDefaultIntervalRetriesFor10MinutesBeforeDroppingBatch() + [Fact] + public void AtTheDefaultIntervalRetriesFor10MinutesBeforeDroppingBatch() + { + var bcs = new BatchedConnectionStatus(DefaultPeriod); + var cumulative = TimeSpan.Zero; + do { - var bcs = new BatchedConnectionStatus(DefaultPeriod); - var cumulative = TimeSpan.Zero; - do - { - bcs.MarkFailure(); + bcs.MarkFailure(); - if (!bcs.ShouldDropBatch) - cumulative += bcs.NextInterval; - } while (!bcs.ShouldDropBatch); + if (!bcs.ShouldDropBatch) + cumulative += bcs.NextInterval; + } while (!bcs.ShouldDropBatch); - Assert.False(bcs.ShouldDropQueue); - Assert.Equal(TimeSpan.Parse("00:10:32", CultureInfo.InvariantCulture), cumulative); - } + Assert.False(bcs.ShouldDropQueue); + Assert.Equal(TimeSpan.Parse("00:10:32", CultureInfo.InvariantCulture), cumulative); + } - [Fact] - public void AtTheDefaultIntervalRetriesFor30MinutesBeforeDroppingQueue() + [Fact] + public void AtTheDefaultIntervalRetriesFor30MinutesBeforeDroppingQueue() + { + var bcs = new BatchedConnectionStatus(DefaultPeriod); + var cumulative = TimeSpan.Zero; + do { - var bcs = new BatchedConnectionStatus(DefaultPeriod); - var cumulative = TimeSpan.Zero; - do - { - bcs.MarkFailure(); + bcs.MarkFailure(); - if (!bcs.ShouldDropQueue) - cumulative += bcs.NextInterval; - } while (!bcs.ShouldDropQueue); + if (!bcs.ShouldDropQueue) + cumulative += bcs.NextInterval; + } while (!bcs.ShouldDropQueue); - Assert.Equal(TimeSpan.Parse("00:30:32", CultureInfo.InvariantCulture), cumulative); - } + Assert.Equal(TimeSpan.Parse("00:30:32", CultureInfo.InvariantCulture), cumulative); } -} +} \ No newline at end of file diff --git a/test/Seq.Extensions.Logging.Tests/Serilog/Sinks/Seq/ControlledLevelSwitchTests.cs b/test/Seq.Extensions.Logging.Tests/Serilog/Sinks/Seq/ControlledLevelSwitchTests.cs index 23ae0f0..ace60a5 100644 --- a/test/Seq.Extensions.Logging.Tests/Serilog/Sinks/Seq/ControlledLevelSwitchTests.cs +++ b/test/Seq.Extensions.Logging.Tests/Serilog/Sinks/Seq/ControlledLevelSwitchTests.cs @@ -4,88 +4,87 @@ using Tests.Support; using Xunit; -namespace Tests.Serilog.Sinks.Seq +namespace Tests.Serilog.Sinks.Seq; + +public class ControlledLevelSwitchTests { - public class ControlledLevelSwitchTests + [Fact] + public void WhenTheServerSendsALevelTheSwitchIsAdjusted() { - [Fact] - public void WhenTheServerSendsALevelTheSwitchIsAdjusted() - { - var lls = new LoggingLevelSwitch(LogLevel.Warning); - var cls = new ControlledLevelSwitch(lls); - cls.Update(LogLevel.Debug); - Assert.Equal(LogLevel.Debug, lls.MinimumLevel); - } + var lls = new LoggingLevelSwitch(LogLevel.Warning); + var cls = new ControlledLevelSwitch(lls); + cls.Update(LogLevel.Debug); + Assert.Equal(LogLevel.Debug, lls.MinimumLevel); + } - [Fact] - public void WhenTheServerSendsNoLevelTheSwitchIsNotInitiallyAdjusted() - { - var lls = new LoggingLevelSwitch(LogLevel.Warning); - lls.MinimumLevel = LogLevel.Critical; - var cls = new ControlledLevelSwitch(lls); - cls.Update(null); - Assert.Equal(LogLevel.Critical, lls.MinimumLevel); - } + [Fact] + public void WhenTheServerSendsNoLevelTheSwitchIsNotInitiallyAdjusted() + { + var lls = new LoggingLevelSwitch(LogLevel.Warning); + lls.MinimumLevel = LogLevel.Critical; + var cls = new ControlledLevelSwitch(lls); + cls.Update(null); + Assert.Equal(LogLevel.Critical, lls.MinimumLevel); + } - [Fact] - public void WhenTheServerSendsNoLevelTheSwitchIsResetIfPreviouslyAdjusted() - { - var lls = new LoggingLevelSwitch(LogLevel.Warning); - var cls = new ControlledLevelSwitch(lls); - cls.Update(LogLevel.Information); - cls.Update(null); - Assert.Equal(LogLevel.Warning, lls.MinimumLevel); - } + [Fact] + public void WhenTheServerSendsNoLevelTheSwitchIsResetIfPreviouslyAdjusted() + { + var lls = new LoggingLevelSwitch(LogLevel.Warning); + var cls = new ControlledLevelSwitch(lls); + cls.Update(LogLevel.Information); + cls.Update(null); + Assert.Equal(LogLevel.Warning, lls.MinimumLevel); + } - [Fact] - public void WithNoSwitchToControlAllEventsAreIncluded() - { - var cls = new ControlledLevelSwitch(null); - Assert.True(cls.IsIncluded(Some.DebugEvent())); - } + [Fact] + public void WithNoSwitchToControlAllEventsAreIncluded() + { + var cls = new ControlledLevelSwitch(null); + Assert.True(cls.IsIncluded(Some.DebugEvent())); + } - [Fact] - public void WithNoSwitchToControlEventsAreStillFiltered() - { - var cls = new ControlledLevelSwitch(null); - cls.Update(LogLevel.Warning); - Assert.True(cls.IsIncluded(Some.ErrorEvent())); - Assert.False(cls.IsIncluded(Some.InformationEvent())); - } + [Fact] + public void WithNoSwitchToControlEventsAreStillFiltered() + { + var cls = new ControlledLevelSwitch(null); + cls.Update(LogLevel.Warning); + Assert.True(cls.IsIncluded(Some.ErrorEvent())); + Assert.False(cls.IsIncluded(Some.InformationEvent())); + } - [Fact] - public void WithNoSwitchToControlAllEventsAreIncludedAfterReset() - { - var cls = new ControlledLevelSwitch(null); - cls.Update(LogLevel.Warning); - cls.Update(null); - Assert.True(cls.IsIncluded(Some.DebugEvent())); - } + [Fact] + public void WithNoSwitchToControlAllEventsAreIncludedAfterReset() + { + var cls = new ControlledLevelSwitch(null); + cls.Update(LogLevel.Warning); + cls.Update(null); + Assert.True(cls.IsIncluded(Some.DebugEvent())); + } - [Fact] - public void WhenControllingASwitchTheControllerIsActive() - { - var cls = new ControlledLevelSwitch(new LoggingLevelSwitch()); - Assert.True(cls.IsActive); - } + [Fact] + public void WhenControllingASwitchTheControllerIsActive() + { + var cls = new ControlledLevelSwitch(new LoggingLevelSwitch()); + Assert.True(cls.IsActive); + } - [Fact] - public void WhenNotControllingASwitchTheControllerIsNotActive() - { - var cls = new ControlledLevelSwitch(); - Assert.False(cls.IsActive); - } + [Fact] + public void WhenNotControllingASwitchTheControllerIsNotActive() + { + var cls = new ControlledLevelSwitch(); + Assert.False(cls.IsActive); + } - [Fact] - public void AfterServerControlhTheControllerIsAlwaysActive() - { - var cls = new ControlledLevelSwitch(); + [Fact] + public void AfterServerControlhTheControllerIsAlwaysActive() + { + var cls = new ControlledLevelSwitch(); - cls.Update(LogLevel.Information); - Assert.True(cls.IsActive); + cls.Update(LogLevel.Information); + Assert.True(cls.IsActive); - cls.Update(null); - Assert.True(cls.IsActive); - } + cls.Update(null); + Assert.True(cls.IsActive); } -} +} \ No newline at end of file diff --git a/test/Seq.Extensions.Logging.Tests/Serilog/Sinks/Seq/SeqPayloadFormatterTests.cs b/test/Seq.Extensions.Logging.Tests/Serilog/Sinks/Seq/SeqPayloadFormatterTests.cs index 876e4c5..1157956 100644 --- a/test/Seq.Extensions.Logging.Tests/Serilog/Sinks/Seq/SeqPayloadFormatterTests.cs +++ b/test/Seq.Extensions.Logging.Tests/Serilog/Sinks/Seq/SeqPayloadFormatterTests.cs @@ -2,25 +2,24 @@ using Tests.Support; using Xunit; -namespace Seq.Extensions.Logging.Tests.Serilog.Sinks.Seq +namespace Seq.Extensions.Logging.Tests.Serilog.Sinks.Seq; + +public class SeqPayloadFormatterTests { - public class SeqPayloadFormatterTests + [Fact] + public void JsonSafeStringPropertiesAreIncludedAsIs() { - [Fact] - public void JsonSafeStringPropertiesAreIncludedAsIs() - { - const string json = "{\"A\": 42}"; - var evt = Some.LogEvent("The answer is {Answer}", new JsonSafeString(json)); - var payload = SeqPayloadFormatter.FormatCompactPayload(new[] { evt }, null); - Assert.Contains("\"Answer\":{\"A\": 42}", payload); - } + const string json = "{\"A\": 42}"; + var evt = Some.LogEvent("The answer is {Answer}", new JsonSafeString(json)); + var payload = SeqPayloadFormatter.FormatCompactPayload(new[] { evt }, null); + Assert.Contains("\"Answer\":{\"A\": 42}", payload); + } - [Fact] - public void DefaultJsonSafeStringsDoNotCorruptPayload() - { - var evt = Some.LogEvent("The answer is {Answer}", (JsonSafeString)default); - var payload = SeqPayloadFormatter.FormatCompactPayload(new[] { evt }, null); - Assert.Contains("\"Answer\":\"\"", payload); - } + [Fact] + public void DefaultJsonSafeStringsDoNotCorruptPayload() + { + var evt = Some.LogEvent("The answer is {Answer}", (JsonSafeString)default); + var payload = SeqPayloadFormatter.FormatCompactPayload(new[] { evt }, null); + Assert.Contains("\"Answer\":\"\"", payload); } } \ No newline at end of file diff --git a/test/Seq.Extensions.Logging.Tests/Support/Some.cs b/test/Seq.Extensions.Logging.Tests/Support/Some.cs index c04101b..b26719d 100644 --- a/test/Seq.Extensions.Logging.Tests/Support/Some.cs +++ b/test/Seq.Extensions.Logging.Tests/Support/Some.cs @@ -1,57 +1,70 @@ using System; using System.Collections.Generic; +using System.Threading; using Serilog.Events; using Xunit.Sdk; using Serilog.Core; using Microsoft.Extensions.Logging; using Serilog.Parameters; +// ReSharper disable MemberCanBePrivate.Global -namespace Tests.Support +namespace Tests.Support; + +static class Some { - static class Some + public static LogEvent LogEvent(string messageTemplate, params object[] propertyValues) { - public static LogEvent LogEvent(string messageTemplate, params object[] propertyValues) - { - return LogEvent(null, messageTemplate, propertyValues); - } + return LogEvent(null, messageTemplate, propertyValues); + } - public static LogEvent LogEvent(Exception exception, string messageTemplate, params object[] propertyValues) - { - return LogEvent(LogLevel.Information, exception, messageTemplate, propertyValues); - } + public static LogEvent LogEvent(Exception exception, string messageTemplate, params object[] propertyValues) + { + return LogEvent(LogLevel.Information, exception, messageTemplate, propertyValues); + } - public static ILogEventPropertyFactory PropertyFactory() - { - return new PropertyValueConverter(10, 1024); - } + public static ILogEventPropertyFactory PropertyFactory() + { + return new PropertyValueConverter(10, 1024); + } - public static LogEvent LogEvent(LogLevel level, Exception exception, string messageTemplate, params object[] propertyValues) - { - var log = new Logger(null, null, null); - MessageTemplate template; - IEnumerable properties; + public static LogEvent LogEvent(LogLevel level, Exception exception, string messageTemplate, params object[] propertyValues) + { + var log = new Logger(null, null, null); + MessageTemplate template; + IEnumerable properties; #pragma warning disable Serilog004 // Constant MessageTemplate verifier - if (!log.BindMessageTemplate(messageTemplate, propertyValues, out template, out properties)) + if (!log.BindMessageTemplate(messageTemplate, propertyValues, out template, out properties)) #pragma warning restore Serilog004 // Constant MessageTemplate verifier - { - throw new XunitException("Template could not be bound."); - } - return new LogEvent(DateTimeOffset.Now, level, exception, template, properties, default, default); - } - - public static LogEvent DebugEvent() { - return LogEvent(LogLevel.Debug, null, "Debug event"); + throw new XunitException("Template could not be bound."); } + return new LogEvent(DateTimeOffset.Now, level, exception, template, properties, default, default); + } - public static LogEvent InformationEvent() - { - return LogEvent(LogLevel.Information, null, "Information event"); - } + public static LogEvent DebugEvent() + { + return LogEvent(LogLevel.Debug, null, "Debug event"); + } - public static LogEvent ErrorEvent(Exception exception = null) - { - return LogEvent(LogLevel.Error, exception, "Error event"); - } + public static LogEvent InformationEvent() + { + return LogEvent(LogLevel.Information, null, "Information event"); + } + + public static LogEvent ErrorEvent(Exception exception = null) + { + return LogEvent(LogLevel.Error, exception, "Error event"); + } + + static int _next; + + public static int Int() + { + return Interlocked.Increment(ref _next); + } + + public static string String() + { + return $"S_{Int()}"; } -} +} \ No newline at end of file From 7aca212121d5524526be30618eeda9403caa476b Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Thu, 25 Jan 2024 13:26:51 +1000 Subject: [PATCH 4/4] Note how to turn off default `TraceId`, `SpanId`, and `ParentId` properties --- README.md | 12 ++++++++++++ example/WebExample/Program.cs | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/README.md b/README.md index bf60de6..4b64c2b 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,18 @@ var json = "{\"A\": 42}"; _logger.LogInformation("The answer is {Answer}", new JsonSafeString(json)); ``` +### Trace and span correlation + +The Seq logger provider automatically adds trace and span ids to events when present, enabling the _Trace_ drop-down menu in Seq's expanded event view. + +ASP.NET Core may add additional top-level `TraceId`, `SpanId`, and `ParentId` properties in its default configuration. You can remove these if you wish, using `ILoggingBuilder.Configure()`: + +```csharp +builder.Logging.Configure(opts => { + opts.ActivityTrackingOptions = ActivityTrackingOptions.None; +}); +``` + ### Troubleshooting > Nothing showed up, what can I do? diff --git a/example/WebExample/Program.cs b/example/WebExample/Program.cs index 3466a03..15ba7cf 100644 --- a/example/WebExample/Program.cs +++ b/example/WebExample/Program.cs @@ -6,6 +6,10 @@ // Use the Seq logging configuration in appsettings.json builder.Logging.AddSeq(); +// Don't log redundant top-level `TraceId` and `SpanId` properties, these are handled implicitly +// by the Seq logger. +builder.Logging.Configure(opts => opts.ActivityTrackingOptions = ActivityTrackingOptions.None); + var app = builder.Build(); // Configure the HTTP request pipeline.