diff --git a/Build.ps1 b/Build.ps1 index cce9432..a674b52 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -25,8 +25,8 @@ foreach ($src in ls src/*) { } else { & dotnet pack -c Release --include-source -o ..\..\artifacts } - - if($LASTEXITCODE -ne 0) { exit 1 } + + if($LASTEXITCODE -ne 0) { exit 1 } Pop-Location } diff --git a/global.json b/global.json index 2c23bfe..d079f83 100644 --- a/global.json +++ b/global.json @@ -1,7 +1,7 @@ { "sdk": { "allowPrerelease": false, - "version": "7.0.100", + "version": "8.0.100", "rollForward": "latestFeature" } } diff --git a/samples/Sample/Program.cs b/samples/Sample/Program.cs index c80e108..22155c4 100644 --- a/samples/Sample/Program.cs +++ b/samples/Sample/Program.cs @@ -1,4 +1,5 @@ using Serilog; +using Serilog.Templates; namespace Sample; @@ -24,7 +25,7 @@ public static int Main(string[] args) } catch (Exception ex) { - Log.Fatal(ex, "An unhandled exception occured during bootstrapping"); + Log.Fatal(ex, "An unhandled exception occurred during bootstrapping"); return 1; } finally @@ -39,6 +40,8 @@ public static IHostBuilder CreateHostBuilder(string[] args) => .ReadFrom.Configuration(context.Configuration) .ReadFrom.Services(services) .Enrich.FromLogContext() - .WriteTo.Console()) - .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); -} \ No newline at end of file + .WriteTo.Console(new ExpressionTemplate( + // Include trace and span ids when present. + "[{@t:HH:mm:ss} {@l:u3}{#if @tr is not null} ({substring(@tr,0,4)}:{substring(@sp,0,4)}){#end}] {@m}\n{@x}"))) + .ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup()); +} diff --git a/samples/Sample/Sample.csproj b/samples/Sample/Sample.csproj index 99ca1cf..c43ac1a 100644 --- a/samples/Sample/Sample.csproj +++ b/samples/Sample/Sample.csproj @@ -1,11 +1,15 @@ - - net7.0 - + + net8.0 + - - - + + + + + + + diff --git a/src/Serilog.AspNetCore/AspNetCore/RequestLoggingMiddleware.cs b/src/Serilog.AspNetCore/AspNetCore/RequestLoggingMiddleware.cs index 0b0b3eb..a0bd873 100644 --- a/src/Serilog.AspNetCore/AspNetCore/RequestLoggingMiddleware.cs +++ b/src/Serilog.AspNetCore/AspNetCore/RequestLoggingMiddleware.cs @@ -32,7 +32,7 @@ class RequestLoggingMiddleware readonly Func> _getMessageTemplateProperties; readonly ILogger? _logger; readonly bool _includeQueryInRequestPath; - static readonly LogEventProperty[] NoProperties = new LogEventProperty[0]; + static readonly LogEventProperty[] NoProperties = Array.Empty(); public RequestLoggingMiddleware(RequestDelegate next, DiagnosticContext diagnosticContext, RequestLoggingOptions options) { @@ -82,7 +82,6 @@ bool LogCompletion(HttpContext httpContext, DiagnosticContextCollector collector if (!logger.IsEnabled(level)) return false; - // Enrich diagnostic context _enrichDiagnosticContext?.Invoke(_diagnosticContext, httpContext); if (!collector.TryComplete(out var collectedProperties, out var collectedException)) @@ -91,7 +90,19 @@ bool LogCompletion(HttpContext httpContext, DiagnosticContextCollector collector // Last-in (correctly) wins... var properties = collectedProperties.Concat(_getMessageTemplateProperties(httpContext, GetPath(httpContext, _includeQueryInRequestPath), elapsedMs, statusCode)); - var evt = new LogEvent(DateTimeOffset.Now, level, ex ?? collectedException, _messageTemplate, properties); + var (traceId, spanId) = Activity.Current is { } activity ? + (activity.TraceId, activity.SpanId) : + (default(ActivityTraceId), default(ActivitySpanId)); + + var evt = new LogEvent( + DateTimeOffset.Now, + level, + ex ?? collectedException, + _messageTemplate, + properties, + traceId, + spanId); + logger.Write(evt); return false; diff --git a/src/Serilog.AspNetCore/AspNetCore/SerilogLoggerFactory.cs b/src/Serilog.AspNetCore/AspNetCore/SerilogLoggerFactory.cs deleted file mode 100644 index 01b3686..0000000 --- a/src/Serilog.AspNetCore/AspNetCore/SerilogLoggerFactory.cs +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2017 Serilog Contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.ComponentModel; -using Microsoft.Extensions.Logging; -using Serilog.Debugging; -using Serilog.Extensions.Logging; - -namespace Serilog.AspNetCore; - -/// -/// Implements so that we can inject Serilog Logger. -/// -[Obsolete("Replaced with Serilog.Extensions.Logging.SerilogLoggerFactory")] -[EditorBrowsable(EditorBrowsableState.Never)] -public class SerilogLoggerFactory : ILoggerFactory -{ - private readonly SerilogLoggerProvider _provider; - - /// - /// Initializes a new instance of the class. - /// - /// The Serilog logger; if not supplied, the static will be used. - /// When true, dispose when the framework disposes the provider. If the - /// logger is not specified but is true, the method will be - /// called on the static class instead. - public SerilogLoggerFactory(ILogger? logger = null, bool dispose = false) - { - _provider = new(logger, dispose); - } - - /// - /// Disposes the provider. - /// - public void Dispose() - { - _provider.Dispose(); - } - - /// - /// Creates a new instance. - /// - /// The category name for messages produced by the logger. - /// - /// The . - /// - public Microsoft.Extensions.Logging.ILogger CreateLogger(string categoryName) - { - return _provider.CreateLogger(categoryName); - } - - /// - /// Adds an to the logging system. - /// - /// The . - public void AddProvider(ILoggerProvider provider) - { - SelfLog.WriteLine("Ignoring added logger provider {0}", provider); - } -} \ No newline at end of file diff --git a/src/Serilog.AspNetCore/Properties/AssemblyInfo.cs b/src/Serilog.AspNetCore/Properties/AssemblyInfo.cs deleted file mode 100644 index b8042d5..0000000 --- a/src/Serilog.AspNetCore/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -[assembly: AssemblyVersion("7.0.0.0")] - -[assembly: InternalsVisibleTo("Serilog.AspNetCore.Tests, PublicKey=" + - "0024000004800000940000000602000000240000525341310004000001000100fb8d13fd344a1c" + - "6fe0fe83ef33c1080bf30690765bc6eb0df26ebfdf8f21670c64265b30db09f73a0dea5b3db4c9" + - "d18dbf6d5a25af5ce9016f281014d79dc3b4201ac646c451830fc7e61a2dfd633d34c39f87b818" + - "94191652df5ac63cc40c77f3542f702bda692e6e8a9158353df189007a49da0f3cfd55eb250066" + - "b19485ec")] diff --git a/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj b/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj index 2e18f90..e963846 100644 --- a/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj +++ b/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj @@ -1,11 +1,11 @@ - + Serilog support for ASP.NET Core logging - 7.0.0 + 8.0.0 Microsoft;Serilog Contributors - net462;netstandard2.0;netstandard2.1;net6.0;net7.0 + net462;netstandard2.0;net6.0;net7.0;net8.0 true serilog;aspnet;aspnetcore icon.png @@ -15,36 +15,41 @@ git false Serilog + README.md + + - + - - + + - + - + - - - - + + + + + + - + - - + + - + diff --git a/src/Serilog.AspNetCore/SerilogWebHostBuilderExtensions.cs b/src/Serilog.AspNetCore/SerilogWebHostBuilderExtensions.cs deleted file mode 100644 index c998f4f..0000000 --- a/src/Serilog.AspNetCore/SerilogWebHostBuilderExtensions.cs +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2017-2019 Serilog Contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Logging; -using Serilog.Extensions.Hosting; -using Serilog.Extensions.Logging; - -namespace Serilog; - -/// -/// Extends with Serilog configuration methods. -/// -public static class SerilogWebHostBuilderExtensions -{ - /// - /// Sets Serilog as the logging provider. - /// - /// The web host builder to configure. - /// The Serilog logger; if not supplied, the static will be used. - /// When true, dispose when the framework disposes the provider. If the - /// logger is not specified but is true, the method will be - /// called on the static class instead. - /// A registered in the Serilog pipeline using the - /// WriteTo.Providers() configuration method, enabling other s to receive events. By - /// default, only Serilog sinks will receive events. - /// The web host builder. - [Obsolete("Prefer UseSerilog() on IHostBuilder")] - public static IWebHostBuilder UseSerilog( - this IWebHostBuilder builder, - ILogger? logger = null, - bool dispose = false, - LoggerProviderCollection? providers = null) - { - if (builder == null) throw new ArgumentNullException(nameof(builder)); - - builder.ConfigureServices(collection => - { - if (providers != null) - { - collection.AddSingleton(services => - { - var factory = new SerilogLoggerFactory(logger, dispose, providers); - - foreach (var provider in services.GetServices()) - factory.AddProvider(provider); - - return factory; - }); - } - else - { - collection.AddSingleton(_ => new SerilogLoggerFactory(logger, dispose)); - } - - ConfigureServices(collection, logger); - }); - - return builder; - } - - /// Sets Serilog as the logging provider. - /// - /// A is supplied so that configuration and hosting information can be used. - /// The logger will be shut down when application services are disposed. - /// - /// The web host builder to configure. - /// The delegate for configuring the that will be used to construct a . - /// Indicates whether to preserve the value of . - /// By default, Serilog does not write events to s registered through - /// the Microsoft.Extensions.Logging API. Normally, equivalent Serilog sinks are used in place of providers. Specify - /// true to write events to all providers. - /// The web host builder. - [Obsolete("Prefer UseSerilog() on IHostBuilder")] - public static IWebHostBuilder UseSerilog( - this IWebHostBuilder builder, - Action configureLogger, - bool preserveStaticLogger = false, - bool writeToProviders = false) - { - if (builder == null) throw new ArgumentNullException(nameof(builder)); - if (configureLogger == null) throw new ArgumentNullException(nameof(configureLogger)); - - builder.ConfigureServices((context, collection) => - { - var loggerConfiguration = new LoggerConfiguration(); - - LoggerProviderCollection? loggerProviders = null; - if (writeToProviders) - { - loggerProviders = new(); - loggerConfiguration.WriteTo.Providers(loggerProviders); - } - - configureLogger(context, loggerConfiguration); - var logger = loggerConfiguration.CreateLogger(); - - ILogger? registeredLogger = null; - if (preserveStaticLogger) - { - registeredLogger = logger; - } - else - { - // Passing a `null` logger to `SerilogLoggerFactory` results in disposal via - // `Log.CloseAndFlush()`, which additionally replaces the static logger with a no-op. - Log.Logger = logger; - } - - collection.AddSingleton(services => - { - var factory = new SerilogLoggerFactory(registeredLogger, true, loggerProviders); - - if (writeToProviders) - { - foreach (var provider in services.GetServices()) - factory.AddProvider(provider); - } - - return factory; - }); - - ConfigureServices(collection, logger); - }); - return builder; - } - - static void ConfigureServices(IServiceCollection collection, ILogger? logger) - { - if (collection == null) throw new ArgumentNullException(nameof(collection)); - - if (logger != null) - { - // This won't (and shouldn't) take ownership of the logger. - collection.AddSingleton(logger); - } - - // Registered to provide two services... - var diagnosticContext = new DiagnosticContext(logger); - - // Consumed by e.g. middleware - collection.AddSingleton(diagnosticContext); - - // Consumed by user code - collection.AddSingleton(diagnosticContext); - } -} \ No newline at end of file diff --git a/test/Serilog.AspNetCore.Tests/Serilog.AspNetCore.Tests.csproj b/test/Serilog.AspNetCore.Tests/Serilog.AspNetCore.Tests.csproj index 0f65b57..446fc77 100644 --- a/test/Serilog.AspNetCore.Tests/Serilog.AspNetCore.Tests.csproj +++ b/test/Serilog.AspNetCore.Tests/Serilog.AspNetCore.Tests.csproj @@ -1,8 +1,7 @@  - net7.0 - true + net8.0 @@ -10,10 +9,10 @@ - - - - + + + + diff --git a/test/Serilog.AspNetCore.Tests/SerilogWebHostBuilderExtensionsTests.cs b/test/Serilog.AspNetCore.Tests/SerilogWebHostBuilderExtensionsTests.cs index 842c2d1..7d4f8a1 100644 --- a/test/Serilog.AspNetCore.Tests/SerilogWebHostBuilderExtensionsTests.cs +++ b/test/Serilog.AspNetCore.Tests/SerilogWebHostBuilderExtensionsTests.cs @@ -160,10 +160,23 @@ WebApplicationFactory Setup( return Task.CompletedTask; }); // 200 OK }) - .UseSerilog(logger, dispose)); + .ConfigureServices(sc => sc.AddSerilog(logger, dispose))); return web; } + + [Fact] + public async Task RequestLoggingMiddlewareShouldAddTraceAndSpanIds() + { + var (sink, web) = Setup(); + + await web.CreateClient().GetAsync("/resource"); + + var completionEvent = sink.Writes.First(logEvent => Matching.FromSource()(logEvent)); + + Assert.NotNull(completionEvent.TraceId); + Assert.NotNull(completionEvent.SpanId); + } (SerilogSink, WebApplicationFactory) Setup( Action? configureOptions = null,