From f1e6499ba0896fccaa80d533e493f07d63e7d115 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Wed, 16 Feb 2022 08:02:42 +1000 Subject: [PATCH 01/12] Dev version bump [skip ci] --- src/Serilog.AspNetCore/Serilog.AspNetCore.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj b/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj index c6ca09e..9764351 100644 --- a/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj +++ b/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj @@ -2,7 +2,7 @@ Serilog support for ASP.NET Core logging - 5.0.0 + 5.0.1 Microsoft;Serilog Contributors netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0 true From e810384e3f21a8559288340c088306e1dfb22a82 Mon Sep 17 00:00:00 2001 From: Igor Fesenko Date: Wed, 23 Feb 2022 16:07:30 -0800 Subject: [PATCH 02/12] Update code snippet for Azure Diagnostics Log Stream configuration Update code snippet to add support for non-Windows based App Service --- README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 4636c62..379d595 100644 --- a/README.md +++ b/README.md @@ -285,11 +285,13 @@ The Azure Diagnostic Log Stream ships events from any files in the `D:\home\LogF .WriteTo.Console() // Add this line: .WriteTo.File( - @"D:\home\LogFiles\Application\myapp.txt", - fileSizeLimitBytes: 1_000_000, - rollOnFileSizeLimit: true, - shared: true, - flushToDiskInterval: TimeSpan.FromSeconds(1)) + System.IO.Path.Combine(Environment.GetEnvironmentVariable("HOME"), "LogFiles", "Application", "diagnostics.txt"), + rollingInterval: RollingInterval.Day, + fileSizeLimitBytes: 10 * 1024 * 1024, + retainedFileCountLimit: 2, + rollOnFileSizeLimit: true, + shared: true, + flushToDiskInterval: TimeSpan.FromSeconds(1)) .CreateLogger(); ``` From 33abd28efab290170f646ca645050a6302924f6f Mon Sep 17 00:00:00 2001 From: Andreas Gullberg Larsen Date: Thu, 4 Nov 2021 18:56:22 +0100 Subject: [PATCH 03/12] Log exception set in IDiagnosticContext If there is no unhandled exception, then log the exception set in IDiagnosticContext. --- .../AspNetCore/RequestLoggingMiddleware.cs | 4 +- .../SerilogWebHostBuilderExtensionsTests.cs | 60 +++++++++++++++++-- 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/src/Serilog.AspNetCore/AspNetCore/RequestLoggingMiddleware.cs b/src/Serilog.AspNetCore/AspNetCore/RequestLoggingMiddleware.cs index cc2d84b..0c53ec8 100644 --- a/src/Serilog.AspNetCore/AspNetCore/RequestLoggingMiddleware.cs +++ b/src/Serilog.AspNetCore/AspNetCore/RequestLoggingMiddleware.cs @@ -86,7 +86,7 @@ bool LogCompletion(HttpContext httpContext, DiagnosticContextCollector collector // Enrich diagnostic context _enrichDiagnosticContext?.Invoke(_diagnosticContext, httpContext); - if (!collector.TryComplete(out var collectedProperties)) + if (!collector.TryComplete(out var collectedProperties, out var collectedException)) collectedProperties = NoProperties; // Last-in (correctly) wins... @@ -98,7 +98,7 @@ bool LogCompletion(HttpContext httpContext, DiagnosticContextCollector collector new LogEventProperty("Elapsed", new ScalarValue(elapsedMs)) }); - var evt = new LogEvent(DateTimeOffset.Now, level, ex, _messageTemplate, properties); + var evt = new LogEvent(DateTimeOffset.Now, level, ex ?? collectedException, _messageTemplate, properties); logger.Write(evt); return false; diff --git a/test/Serilog.AspNetCore.Tests/SerilogWebHostBuilderExtensionsTests.cs b/test/Serilog.AspNetCore.Tests/SerilogWebHostBuilderExtensionsTests.cs index e3ba4a0..0193263 100644 --- a/test/Serilog.AspNetCore.Tests/SerilogWebHostBuilderExtensionsTests.cs +++ b/test/Serilog.AspNetCore.Tests/SerilogWebHostBuilderExtensionsTests.cs @@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; using Serilog.Filters; using Serilog.AspNetCore.Tests.Support; @@ -65,7 +66,51 @@ public async Task RequestLoggingMiddlewareShouldEnrich() Assert.True(completionEvent.Properties.ContainsKey("Elapsed")); } - WebApplicationFactory Setup(ILogger logger, bool dispose, Action configureOptions = null) + [Fact] + public async Task RequestLoggingMiddlewareShouldEnrichWithCollectedExceptionIfNoUnhandledException() + { + var diagnosticContextException = new Exception("Exception set in diagnostic context"); + var (sink, web) = Setup(options => + { + options.EnrichDiagnosticContext += (diagnosticContext, _) => + { + diagnosticContext.SetException(diagnosticContextException); + }; + }); + + await web.CreateClient().GetAsync("/resource"); + + var completionEvent = sink.Writes.First(logEvent => Matching.FromSource()(logEvent)); + + Assert.Same(diagnosticContextException, completionEvent.Exception); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task RequestLoggingMiddlewareShouldEnrichWithUnhandledExceptionEvenIfExceptionIsSetInDiagnosticContext(bool setExceptionInDiagnosticContext) + { + var diagnosticContextException = new Exception("Exception set in diagnostic context"); + var unhandledException = new Exception("Unhandled exception thrown in API action"); + var (sink, web) = Setup(options => + { + options.EnrichDiagnosticContext += (diagnosticContext, _) => + { + if (setExceptionInDiagnosticContext) + diagnosticContext.SetException(diagnosticContextException); + }; + }, actionCallback: _ => throw unhandledException); + + Func act = () => web.CreateClient().GetAsync("/resource"); + + Exception thrownException = await Assert.ThrowsAsync(act); + var completionEvent = sink.Writes.First(logEvent => Matching.FromSource()(logEvent)); + Assert.Same(unhandledException, completionEvent.Exception); + Assert.Same(unhandledException, thrownException); + } + + WebApplicationFactory Setup(ILogger logger, bool dispose, Action configureOptions = null, + Action actionCallback = null) { var web = _web.WithWebHostBuilder( builder => builder @@ -80,14 +125,19 @@ WebApplicationFactory Setup(ILogger logger, bool dispose, Action { app.UseSerilogRequestLogging(configureOptions); - app.Run(_ => Task.CompletedTask); // 200 OK + app.Run(ctx => + { + actionCallback?.Invoke(ctx); + return Task.CompletedTask; + }); // 200 OK }) .UseSerilog(logger, dispose)); return web; } - (SerilogSink, WebApplicationFactory) Setup(Action configureOptions = null) + (SerilogSink, WebApplicationFactory) Setup(Action configureOptions = null, + Action actionCallback = null) { var sink = new SerilogSink(); var logger = new LoggerConfiguration() @@ -95,9 +145,9 @@ WebApplicationFactory Setup(ILogger logger, bool dispose, Action Date: Thu, 3 Mar 2022 13:14:10 +0100 Subject: [PATCH 04/12] Upgrade to Serilog.Extensions.Hosting 4.2.1-dev-00092 --- src/Serilog.AspNetCore/Serilog.AspNetCore.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj b/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj index 9764351..e9f048d 100644 --- a/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj +++ b/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj @@ -30,7 +30,7 @@ - + From 532fd1b5d18805832b7ef69ed99b3ef5ed438ca9 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Sat, 5 Mar 2022 07:38:38 +1000 Subject: [PATCH 05/12] Bump major version; breaking changes in #271 --- src/Serilog.AspNetCore/Serilog.AspNetCore.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj b/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj index e9f048d..7e3549e 100644 --- a/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj +++ b/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj @@ -2,7 +2,7 @@ Serilog support for ASP.NET Core logging - 5.0.1 + 6.0.0 Microsoft;Serilog Contributors netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0 true @@ -30,7 +30,7 @@ - + From 91080dff4046f2d77f96653cf6d13953b3f1ae72 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Fri, 15 Jul 2022 08:53:29 +1000 Subject: [PATCH 06/12] Version 5.0.0 of Serilog.Extensions.Hosting is now released --- src/Serilog.AspNetCore/Serilog.AspNetCore.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj b/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj index 7e3549e..0dfd0d5 100644 --- a/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj +++ b/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj @@ -30,7 +30,7 @@ - + From e4bc34237ac0eafc2c4c5ce9d4bb1f4afb9cb88d Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Tue, 19 Jul 2022 09:25:42 +1000 Subject: [PATCH 07/12] Update to fixed Serilog.Extensions.Hosting version 5.0.1 --- src/Serilog.AspNetCore/Serilog.AspNetCore.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj b/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj index 0dfd0d5..6be5d5f 100644 --- a/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj +++ b/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj @@ -2,7 +2,7 @@ Serilog support for ASP.NET Core logging - 6.0.0 + 6.0.1 Microsoft;Serilog Contributors netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0 true @@ -30,7 +30,7 @@ - + From ccd73c309bf61be35a6f7a31602e57526076c16a Mon Sep 17 00:00:00 2001 From: David Perfors Date: Mon, 29 Aug 2022 21:15:59 +0200 Subject: [PATCH 08/12] Add GetMessageTemplateProperties option --- .../AspNetCore/RequestLoggingMiddleware.cs | 13 ++++---- .../AspNetCore/RequestLoggingOptions.cs | 16 ++++++++++ .../SerilogWebHostBuilderExtensionsTests.cs | 30 +++++++++++++++++++ 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/src/Serilog.AspNetCore/AspNetCore/RequestLoggingMiddleware.cs b/src/Serilog.AspNetCore/AspNetCore/RequestLoggingMiddleware.cs index 0c53ec8..9b9f61c 100644 --- a/src/Serilog.AspNetCore/AspNetCore/RequestLoggingMiddleware.cs +++ b/src/Serilog.AspNetCore/AspNetCore/RequestLoggingMiddleware.cs @@ -18,6 +18,7 @@ using Serilog.Extensions.Hosting; using Serilog.Parsing; using System; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; @@ -32,6 +33,7 @@ class RequestLoggingMiddleware readonly MessageTemplate _messageTemplate; readonly Action _enrichDiagnosticContext; readonly Func _getLevel; + readonly Func> _getMessageTemplateProperties; readonly ILogger _logger; readonly bool _includeQueryInRequestPath; static readonly LogEventProperty[] NoProperties = new LogEventProperty[0]; @@ -47,6 +49,7 @@ public RequestLoggingMiddleware(RequestDelegate next, DiagnosticContext diagnost _messageTemplate = new MessageTemplateParser().Parse(options.MessageTemplate); _logger = options.Logger?.ForContext(); _includeQueryInRequestPath = options.IncludeQueryInRequestPath; + _getMessageTemplateProperties = options.GetMessageTemplateProperties; } // ReSharper disable once UnusedMember.Global @@ -90,13 +93,7 @@ bool LogCompletion(HttpContext httpContext, DiagnosticContextCollector collector collectedProperties = NoProperties; // Last-in (correctly) wins... - var properties = collectedProperties.Concat(new[] - { - new LogEventProperty("RequestMethod", new ScalarValue(httpContext.Request.Method)), - new LogEventProperty("RequestPath", new ScalarValue(GetPath(httpContext, _includeQueryInRequestPath))), - new LogEventProperty("StatusCode", new ScalarValue(statusCode)), - new LogEventProperty("Elapsed", new ScalarValue(elapsedMs)) - }); + var properties = collectedProperties.Concat(_getMessageTemplateProperties(httpContext, GetPath(httpContext, _includeQueryInRequestPath), elapsedMs, statusCode)); var evt = new LogEvent(DateTimeOffset.Now, level, ex ?? collectedException, _messageTemplate, properties); logger.Write(evt); @@ -123,7 +120,7 @@ static string GetPath(HttpContext httpContext, bool includeQueryInRequestPath) { requestPath = httpContext.Request.Path.ToString(); } - + return requestPath; } } diff --git a/src/Serilog.AspNetCore/AspNetCore/RequestLoggingOptions.cs b/src/Serilog.AspNetCore/AspNetCore/RequestLoggingOptions.cs index bd6fead..2227388 100644 --- a/src/Serilog.AspNetCore/AspNetCore/RequestLoggingOptions.cs +++ b/src/Serilog.AspNetCore/AspNetCore/RequestLoggingOptions.cs @@ -15,6 +15,7 @@ using Microsoft.AspNetCore.Http; using Serilog.Events; using System; +using System.Collections.Generic; // ReSharper disable UnusedAutoPropertyAccessor.Global @@ -34,6 +35,15 @@ static LogEventLevel DefaultGetLevel(HttpContext ctx, double _, Exception ex) => : ctx.Response.StatusCode > 499 ? LogEventLevel.Error : LogEventLevel.Information; + + IEnumerable DefaultGetMessageTemplateProperties(HttpContext httpContext, string requestPath, double elapsedMs, int statusCode) => + new[] + { + new LogEventProperty("RequestMethod", new ScalarValue(httpContext.Request.Method)), + new LogEventProperty("RequestPath", new ScalarValue(requestPath)), + new LogEventProperty("StatusCode", new ScalarValue(statusCode)), + new LogEventProperty("Elapsed", new ScalarValue(elapsedMs)) + }; /// /// Gets or sets the message template. The default value is @@ -74,6 +84,11 @@ static LogEventLevel DefaultGetLevel(HttpContext ctx, double _, Exception ex) => /// public bool IncludeQueryInRequestPath { get; set; } + /// + /// A function to specify the values of the MessageTemplateProperties. + /// + public Func> GetMessageTemplateProperties { get; set; } + /// /// Constructor /// @@ -81,6 +96,7 @@ public RequestLoggingOptions() { GetLevel = DefaultGetLevel; MessageTemplate = DefaultRequestCompletionMessageTemplate; + GetMessageTemplateProperties = DefaultGetMessageTemplateProperties; } } } diff --git a/test/Serilog.AspNetCore.Tests/SerilogWebHostBuilderExtensionsTests.cs b/test/Serilog.AspNetCore.Tests/SerilogWebHostBuilderExtensionsTests.cs index 0193263..09f6c1b 100644 --- a/test/Serilog.AspNetCore.Tests/SerilogWebHostBuilderExtensionsTests.cs +++ b/test/Serilog.AspNetCore.Tests/SerilogWebHostBuilderExtensionsTests.cs @@ -12,6 +12,8 @@ using Microsoft.AspNetCore.Http; using Serilog.Filters; using Serilog.AspNetCore.Tests.Support; +using Serilog.Events; +using System.Net.Http; // Newer frameworks provide IHostBuilder #pragma warning disable CS0618 @@ -66,6 +68,34 @@ public async Task RequestLoggingMiddlewareShouldEnrich() Assert.True(completionEvent.Properties.ContainsKey("Elapsed")); } + [Fact] + public async Task RequestLoggingMiddlewareShouldEnrichWithCustomisedProperties() + { + var (sink, web) = Setup(options => + { + options.MessageTemplate = "HTTP {RequestMethod} responded {Status} in {ElapsedMilliseconds:0.0000} ms"; + options.GetMessageTemplateProperties = (ctx, path, elapsedMs, status) => + new[] + { + new LogEventProperty("RequestMethod", new ScalarValue(ctx.Request.Method)), + new LogEventProperty("Status", new ScalarValue(status)), + new LogEventProperty("ElapsedMilliseconds", new ScalarValue(elapsedMs)) + }; + }); + + await web.CreateClient().GetAsync("/resource"); + + Assert.NotEmpty(sink.Writes); + + var completionEvent = sink.Writes.First(logEvent => Matching.FromSource()(logEvent)); + + Assert.Equal("string", completionEvent.Properties["SomeString"].LiteralValue()); + Assert.Equal(200, completionEvent.Properties["Status"].LiteralValue()); + Assert.Equal("GET", completionEvent.Properties["RequestMethod"].LiteralValue()); + Assert.True(completionEvent.Properties.ContainsKey("ElapsedMilliseconds")); + Assert.False(completionEvent.Properties.ContainsKey("Elapsed")); + } + [Fact] public async Task RequestLoggingMiddlewareShouldEnrichWithCollectedExceptionIfNoUnhandledException() { From f56c26cb08497394e56d932df0bd8bdc84f49dd4 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Tue, 30 Aug 2022 06:46:26 +1000 Subject: [PATCH 09/12] Minor version bump - new option added --- src/Serilog.AspNetCore/Serilog.AspNetCore.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj b/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj index 6be5d5f..1635423 100644 --- a/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj +++ b/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj @@ -2,7 +2,7 @@ Serilog support for ASP.NET Core logging - 6.0.1 + 6.1.0 Microsoft;Serilog Contributors netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0 true From 55d2469f09c86bbf142317b38c6995d407735be3 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Sat, 17 Sep 2022 21:20:26 +1000 Subject: [PATCH 10/12] enable nullable --- Directory.Build.props | 12 + global.json | 2 +- samples/Sample/Controllers/HomeController.cs | 73 +- samples/Sample/Models/ErrorViewModel.cs | 11 +- samples/Sample/Program.cs | 81 +- samples/Sample/Sample.csproj | 2 +- samples/Sample/Startup.cs | 87 +-- serilog-aspnetcore.sln | 1 + .../AspNetCore/RequestLoggingMiddleware.cs | 171 +++-- .../AspNetCore/RequestLoggingOptions.cs | 141 ++-- .../AspNetCore/SerilogLoggerFactory.cs | 90 ++- .../Serilog.AspNetCore.csproj | 4 - .../SerilogApplicationBuilderExtensions.cs | 93 ++- .../SerilogWebHostBuilderExtensions.cs | 222 +++--- .../Serilog.AspNetCore.Tests.csproj | 8 +- .../SerilogWebHostBuilderExtensionsTests.cs | 259 ++++--- .../Support/DisposeTrackingLogger.cs | 702 +++++++++--------- .../Support/Extensions.cs | 13 +- .../Support/SerilogSink.cs | 16 +- .../Support/SerilogWebApplicationFactory.cs | 17 +- 20 files changed, 980 insertions(+), 1025 deletions(-) create mode 100644 Directory.Build.props diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..3dcd183 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,12 @@ + + + latest + True + true + ../../assets/Serilog.snk + true + false + enable + enable + + diff --git a/global.json b/global.json index 207e9f1..99ee408 100644 --- a/global.json +++ b/global.json @@ -1,7 +1,7 @@ { "sdk": { "allowPrerelease": false, - "version": "5.0.201", + "version": "6.0.401", "rollForward": "latestFeature" } } diff --git a/samples/Sample/Controllers/HomeController.cs b/samples/Sample/Controllers/HomeController.cs index 9f1308d..ec4ab25 100644 --- a/samples/Sample/Controllers/HomeController.cs +++ b/samples/Sample/Controllers/HomeController.cs @@ -1,47 +1,40 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; +using System.Diagnostics; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; using Sample.Models; using Serilog; -namespace Sample.Controllers +namespace Sample.Controllers; + +public class HomeController : Controller { - public class HomeController : Controller + static int _callCount; + + readonly ILogger _logger; + readonly IDiagnosticContext _diagnosticContext; + + public HomeController(ILogger logger, IDiagnosticContext diagnosticContext) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _diagnosticContext = diagnosticContext ?? throw new ArgumentNullException(nameof(diagnosticContext)); + } + + public IActionResult Index() + { + _logger.LogInformation("Hello, world!"); + + _diagnosticContext.Set("IndexCallCount", Interlocked.Increment(ref _callCount)); + + return View(); + } + + public IActionResult Privacy() + { + throw new InvalidOperationException("Something went wrong."); + } + + [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] + public IActionResult Error() { - static int _callCount; - - readonly ILogger _logger; - readonly IDiagnosticContext _diagnosticContext; - - public HomeController(ILogger logger, IDiagnosticContext diagnosticContext) - { - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _diagnosticContext = diagnosticContext ?? throw new ArgumentNullException(nameof(diagnosticContext)); - } - - public IActionResult Index() - { - _logger.LogInformation("Hello, world!"); - - _diagnosticContext.Set("IndexCallCount", Interlocked.Increment(ref _callCount)); - - return View(); - } - - public IActionResult Privacy() - { - throw new InvalidOperationException("Something went wrong."); - } - - [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] - public IActionResult Error() - { - return View(new ErrorViewModel {RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier}); - } + return View(new ErrorViewModel {RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier}); } -} +} \ No newline at end of file diff --git a/samples/Sample/Models/ErrorViewModel.cs b/samples/Sample/Models/ErrorViewModel.cs index 68f449e..3fc26f0 100644 --- a/samples/Sample/Models/ErrorViewModel.cs +++ b/samples/Sample/Models/ErrorViewModel.cs @@ -1,11 +1,8 @@ -using System; +namespace Sample.Models; -namespace Sample.Models +public class ErrorViewModel { - public class ErrorViewModel - { - public string RequestId { get; set; } + public string? RequestId { get; set; } - public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); - } + public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); } \ No newline at end of file diff --git a/samples/Sample/Program.cs b/samples/Sample/Program.cs index 2bd5e57..c80e108 100644 --- a/samples/Sample/Program.cs +++ b/samples/Sample/Program.cs @@ -1,53 +1,44 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; using Serilog; -namespace Sample +namespace Sample; + +public static class Program { - public static class Program + public static int Main(string[] args) { - public static int Main(string[] args) - { - // The initial "bootstrap" logger is able to log errors during start-up. It's completely replaced by the - // logger configured in `UseSerilog()` below, once configuration and dependency-injection have both been - // set up successfully. - Log.Logger = new LoggerConfiguration() - .WriteTo.Console() - .CreateBootstrapLogger(); - - Log.Information("Starting up!"); + // The initial "bootstrap" logger is able to log errors during start-up. It's completely replaced by the + // logger configured in `UseSerilog()` below, once configuration and dependency-injection have both been + // set up successfully. + Log.Logger = new LoggerConfiguration() + .WriteTo.Console() + .CreateBootstrapLogger(); - try - { - CreateHostBuilder(args).Build().Run(); + Log.Information("Starting up!"); - Log.Information("Stopped cleanly"); - return 0; - } - catch (Exception ex) - { - Log.Fatal(ex, "An unhandled exception occured during bootstrapping"); - return 1; - } - finally - { - Log.CloseAndFlush(); - } - } + try + { + CreateHostBuilder(args).Build().Run(); - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .UseSerilog((context, services, configuration) => configuration - .ReadFrom.Configuration(context.Configuration) - .ReadFrom.Services(services) - .Enrich.FromLogContext() - .WriteTo.Console()) - .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); + Log.Information("Stopped cleanly"); + return 0; + } + catch (Exception ex) + { + Log.Fatal(ex, "An unhandled exception occured during bootstrapping"); + return 1; + } + finally + { + Log.CloseAndFlush(); + } } -} + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseSerilog((context, services, configuration) => configuration + .ReadFrom.Configuration(context.Configuration) + .ReadFrom.Services(services) + .Enrich.FromLogContext() + .WriteTo.Console()) + .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); +} \ No newline at end of file diff --git a/samples/Sample/Sample.csproj b/samples/Sample/Sample.csproj index dff37ac..f32c84b 100644 --- a/samples/Sample/Sample.csproj +++ b/samples/Sample/Sample.csproj @@ -1,7 +1,7 @@ - net5.0 + net6.0 diff --git a/samples/Sample/Startup.cs b/samples/Sample/Startup.cs index 6c29268..67200ce 100644 --- a/samples/Sample/Startup.cs +++ b/samples/Sample/Startup.cs @@ -1,64 +1,53 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.HttpsPolicy; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; using Serilog; -namespace Sample +namespace Sample; + +public class Startup { - public class Startup + public Startup(IConfiguration configuration) { - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } + Configuration = configuration; + } - public IConfiguration Configuration { get; } + public IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddControllersWithViews(); + } - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) { - services.AddControllersWithViews(); + app.UseDeveloperExceptionPage(); } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + else { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - else - { - app.UseExceptionHandler("/Home/Error"); - // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. - app.UseHsts(); - } + app.UseExceptionHandler("/Home/Error"); + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + app.UseHsts(); + } - app.UseHttpsRedirection(); - app.UseStaticFiles(); - - // Write streamlined request completion events, instead of the more verbose ones from the framework. - // To use the default framework request logging instead, remove this line and set the "Microsoft" - // level in appsettings.json to "Information". - app.UseSerilogRequestLogging(); + app.UseHttpsRedirection(); + app.UseStaticFiles(); - app.UseRouting(); + // Write streamlined request completion events, instead of the more verbose ones from the framework. + // To use the default framework request logging instead, remove this line and set the "Microsoft" + // level in appsettings.json to "Information". + app.UseSerilogRequestLogging(); - app.UseAuthorization(); + app.UseRouting(); - app.UseEndpoints(endpoints => - { - endpoints.MapControllerRoute( - name: "default", - pattern: "{controller=Home}/{action=Index}/{id?}"); - }); - } + app.UseAuthorization(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllerRoute( + name: "default", + pattern: "{controller=Home}/{action=Index}/{id?}"); + }); } } \ No newline at end of file diff --git a/serilog-aspnetcore.sln b/serilog-aspnetcore.sln index bc01e54..a9b5e42 100644 --- a/serilog-aspnetcore.sln +++ b/serilog-aspnetcore.sln @@ -17,6 +17,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "assets", "assets", "{9C21B9 README.md = README.md assets\Serilog.snk = assets\Serilog.snk Setup.ps1 = Setup.ps1 + Directory.Build.props = Directory.Build.props EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.AspNetCore", "src\Serilog.AspNetCore\Serilog.AspNetCore.csproj", "{0549D23F-986B-4FB2-BACE-16FD7A7BC9EF}" diff --git a/src/Serilog.AspNetCore/AspNetCore/RequestLoggingMiddleware.cs b/src/Serilog.AspNetCore/AspNetCore/RequestLoggingMiddleware.cs index 9b9f61c..0b0b3eb 100644 --- a/src/Serilog.AspNetCore/AspNetCore/RequestLoggingMiddleware.cs +++ b/src/Serilog.AspNetCore/AspNetCore/RequestLoggingMiddleware.cs @@ -17,111 +17,106 @@ using Serilog.Events; using Serilog.Extensions.Hosting; using Serilog.Parsing; -using System; -using System.Collections.Generic; using System.Diagnostics; -using System.Linq; -using System.Threading.Tasks; -namespace Serilog.AspNetCore +namespace Serilog.AspNetCore; + +// ReSharper disable once ClassNeverInstantiated.Global +class RequestLoggingMiddleware { - // ReSharper disable once ClassNeverInstantiated.Global - class RequestLoggingMiddleware + readonly RequestDelegate _next; + readonly DiagnosticContext _diagnosticContext; + readonly MessageTemplate _messageTemplate; + readonly Action? _enrichDiagnosticContext; + readonly Func _getLevel; + readonly Func> _getMessageTemplateProperties; + readonly ILogger? _logger; + readonly bool _includeQueryInRequestPath; + static readonly LogEventProperty[] NoProperties = new LogEventProperty[0]; + + public RequestLoggingMiddleware(RequestDelegate next, DiagnosticContext diagnosticContext, RequestLoggingOptions options) { - readonly RequestDelegate _next; - readonly DiagnosticContext _diagnosticContext; - readonly MessageTemplate _messageTemplate; - readonly Action _enrichDiagnosticContext; - readonly Func _getLevel; - readonly Func> _getMessageTemplateProperties; - readonly ILogger _logger; - readonly bool _includeQueryInRequestPath; - static readonly LogEventProperty[] NoProperties = new LogEventProperty[0]; - - public RequestLoggingMiddleware(RequestDelegate next, DiagnosticContext diagnosticContext, RequestLoggingOptions options) + if (options == null) throw new ArgumentNullException(nameof(options)); + _next = next ?? throw new ArgumentNullException(nameof(next)); + _diagnosticContext = diagnosticContext ?? throw new ArgumentNullException(nameof(diagnosticContext)); + + _getLevel = options.GetLevel; + _enrichDiagnosticContext = options.EnrichDiagnosticContext; + _messageTemplate = new MessageTemplateParser().Parse(options.MessageTemplate); + _logger = options.Logger?.ForContext(); + _includeQueryInRequestPath = options.IncludeQueryInRequestPath; + _getMessageTemplateProperties = options.GetMessageTemplateProperties; + } + + // ReSharper disable once UnusedMember.Global + public async Task Invoke(HttpContext httpContext) + { + if (httpContext == null) throw new ArgumentNullException(nameof(httpContext)); + + var start = Stopwatch.GetTimestamp(); + + var collector = _diagnosticContext.BeginCollection(); + try { - if (options == null) throw new ArgumentNullException(nameof(options)); - _next = next ?? throw new ArgumentNullException(nameof(next)); - _diagnosticContext = diagnosticContext ?? throw new ArgumentNullException(nameof(diagnosticContext)); - - _getLevel = options.GetLevel; - _enrichDiagnosticContext = options.EnrichDiagnosticContext; - _messageTemplate = new MessageTemplateParser().Parse(options.MessageTemplate); - _logger = options.Logger?.ForContext(); - _includeQueryInRequestPath = options.IncludeQueryInRequestPath; - _getMessageTemplateProperties = options.GetMessageTemplateProperties; + await _next(httpContext); + var elapsedMs = GetElapsedMilliseconds(start, Stopwatch.GetTimestamp()); + var statusCode = httpContext.Response.StatusCode; + LogCompletion(httpContext, collector, statusCode, elapsedMs, null); } - - // ReSharper disable once UnusedMember.Global - public async Task Invoke(HttpContext httpContext) + catch (Exception ex) + // Never caught, because `LogCompletion()` returns false. This ensures e.g. the developer exception page is still + // shown, although it does also mean we see a duplicate "unhandled exception" event from ASP.NET Core. + when (LogCompletion(httpContext, collector, 500, GetElapsedMilliseconds(start, Stopwatch.GetTimestamp()), ex)) { - if (httpContext == null) throw new ArgumentNullException(nameof(httpContext)); - - var start = Stopwatch.GetTimestamp(); - - var collector = _diagnosticContext.BeginCollection(); - try - { - await _next(httpContext); - var elapsedMs = GetElapsedMilliseconds(start, Stopwatch.GetTimestamp()); - var statusCode = httpContext.Response.StatusCode; - LogCompletion(httpContext, collector, statusCode, elapsedMs, null); - } - catch (Exception ex) - // Never caught, because `LogCompletion()` returns false. This ensures e.g. the developer exception page is still - // shown, although it does also mean we see a duplicate "unhandled exception" event from ASP.NET Core. - when (LogCompletion(httpContext, collector, 500, GetElapsedMilliseconds(start, Stopwatch.GetTimestamp()), ex)) - { - } - finally - { - collector.Dispose(); - } } - - bool LogCompletion(HttpContext httpContext, DiagnosticContextCollector collector, int statusCode, double elapsedMs, Exception ex) + finally { - var logger = _logger ?? Log.ForContext(); - var level = _getLevel(httpContext, elapsedMs, ex); + collector.Dispose(); + } + } - if (!logger.IsEnabled(level)) return false; + bool LogCompletion(HttpContext httpContext, DiagnosticContextCollector collector, int statusCode, double elapsedMs, Exception? ex) + { + var logger = _logger ?? Log.ForContext(); + var level = _getLevel(httpContext, elapsedMs, ex); - // Enrich diagnostic context - _enrichDiagnosticContext?.Invoke(_diagnosticContext, httpContext); + if (!logger.IsEnabled(level)) return false; - if (!collector.TryComplete(out var collectedProperties, out var collectedException)) - collectedProperties = NoProperties; + // Enrich diagnostic context + _enrichDiagnosticContext?.Invoke(_diagnosticContext, httpContext); - // Last-in (correctly) wins... - var properties = collectedProperties.Concat(_getMessageTemplateProperties(httpContext, GetPath(httpContext, _includeQueryInRequestPath), elapsedMs, statusCode)); + if (!collector.TryComplete(out var collectedProperties, out var collectedException)) + collectedProperties = NoProperties; - var evt = new LogEvent(DateTimeOffset.Now, level, ex ?? collectedException, _messageTemplate, properties); - logger.Write(evt); + // Last-in (correctly) wins... + var properties = collectedProperties.Concat(_getMessageTemplateProperties(httpContext, GetPath(httpContext, _includeQueryInRequestPath), elapsedMs, statusCode)); - return false; - } + var evt = new LogEvent(DateTimeOffset.Now, level, ex ?? collectedException, _messageTemplate, properties); + logger.Write(evt); - static double GetElapsedMilliseconds(long start, long stop) - { - return (stop - start) * 1000 / (double)Stopwatch.Frequency; - } + return false; + } - static string GetPath(HttpContext httpContext, bool includeQueryInRequestPath) + static double GetElapsedMilliseconds(long start, long stop) + { + return (stop - start) * 1000 / (double)Stopwatch.Frequency; + } + + static string GetPath(HttpContext httpContext, bool includeQueryInRequestPath) + { + /* + In some cases, like when running integration tests with WebApplicationFactory + the Path returns an empty string instead of null, in that case we can't use + ?? as fallback. + */ + var requestPath = includeQueryInRequestPath + ? httpContext.Features.Get()?.RawTarget + : httpContext.Features.Get()?.Path; + if (string.IsNullOrEmpty(requestPath)) { - /* - In some cases, like when running integration tests with WebApplicationFactory - the Path returns an empty string instead of null, in that case we can't use - ?? as fallback. - */ - var requestPath = includeQueryInRequestPath - ? httpContext.Features.Get()?.RawTarget - : httpContext.Features.Get()?.Path; - if (string.IsNullOrEmpty(requestPath)) - { - requestPath = httpContext.Request.Path.ToString(); - } - - return requestPath; + requestPath = httpContext.Request.Path.ToString(); } + + return requestPath!; } -} +} \ No newline at end of file diff --git a/src/Serilog.AspNetCore/AspNetCore/RequestLoggingOptions.cs b/src/Serilog.AspNetCore/AspNetCore/RequestLoggingOptions.cs index 2227388..d782e6d 100644 --- a/src/Serilog.AspNetCore/AspNetCore/RequestLoggingOptions.cs +++ b/src/Serilog.AspNetCore/AspNetCore/RequestLoggingOptions.cs @@ -14,89 +14,86 @@ using Microsoft.AspNetCore.Http; using Serilog.Events; -using System; -using System.Collections.Generic; // ReSharper disable UnusedAutoPropertyAccessor.Global -namespace Serilog.AspNetCore +namespace Serilog.AspNetCore; + +/// +/// Contains options for the . +/// +public class RequestLoggingOptions { - /// - /// Contains options for the . - /// - public class RequestLoggingOptions - { - const string DefaultRequestCompletionMessageTemplate = - "HTTP {RequestMethod} {RequestPath} responded {StatusCode} in {Elapsed:0.0000} ms"; + const string DefaultRequestCompletionMessageTemplate = + "HTTP {RequestMethod} {RequestPath} responded {StatusCode} in {Elapsed:0.0000} ms"; - static LogEventLevel DefaultGetLevel(HttpContext ctx, double _, Exception ex) => - ex != null + static LogEventLevel DefaultGetLevel(HttpContext ctx, double _, Exception? ex) => + ex != null + ? LogEventLevel.Error + : ctx.Response.StatusCode > 499 ? LogEventLevel.Error - : ctx.Response.StatusCode > 499 - ? LogEventLevel.Error - : LogEventLevel.Information; - - IEnumerable DefaultGetMessageTemplateProperties(HttpContext httpContext, string requestPath, double elapsedMs, int statusCode) => - new[] - { - new LogEventProperty("RequestMethod", new ScalarValue(httpContext.Request.Method)), - new LogEventProperty("RequestPath", new ScalarValue(requestPath)), - new LogEventProperty("StatusCode", new ScalarValue(statusCode)), - new LogEventProperty("Elapsed", new ScalarValue(elapsedMs)) - }; + : LogEventLevel.Information; - /// - /// Gets or sets the message template. The default value is - /// "HTTP {RequestMethod} {RequestPath} responded {StatusCode} in {Elapsed:0.0000} ms". The - /// template can contain any of the placeholders from the default template, names of properties - /// added by ASP.NET Core, and names of properties added to the . - /// - /// - /// The message template. - /// - public string MessageTemplate { get; set; } + static IEnumerable DefaultGetMessageTemplateProperties(HttpContext httpContext, string requestPath, double elapsedMs, int statusCode) => + new[] + { + new LogEventProperty("RequestMethod", new ScalarValue(httpContext.Request.Method)), + new LogEventProperty("RequestPath", new ScalarValue(requestPath)), + new LogEventProperty("StatusCode", new ScalarValue(statusCode)), + new LogEventProperty("Elapsed", new ScalarValue(elapsedMs)) + }; - /// - /// A function returning the based on the , the number of - /// elapsed milliseconds required for handling the request, and an if one was thrown. - /// The default behavior returns when the response status code is greater than 499 or if the - /// is not null. - /// - /// - /// A function returning the . - /// - public Func GetLevel { get; set; } + /// + /// Gets or sets the message template. The default value is + /// "HTTP {RequestMethod} {RequestPath} responded {StatusCode} in {Elapsed:0.0000} ms". The + /// template can contain any of the placeholders from the default template, names of properties + /// added by ASP.NET Core, and names of properties added to the . + /// + /// + /// The message template. + /// + public string MessageTemplate { get; set; } - /// - /// A callback that can be used to set additional properties on the request completion event. - /// - public Action EnrichDiagnosticContext { get; set; } + /// + /// A function returning the based on the , the number of + /// elapsed milliseconds required for handling the request, and an if one was thrown. + /// The default behavior returns when the response status code is greater than 499 or if the + /// is not null. + /// + /// + /// A function returning the . + /// + public Func GetLevel { get; set; } + + /// + /// A callback that can be used to set additional properties on the request completion event. + /// + public Action? EnrichDiagnosticContext { get; set; } - /// - /// The logger through which request completion events will be logged. The default is to use the - /// static class. - /// - public ILogger Logger { get; set; } + /// + /// The logger through which request completion events will be logged. The default is to use the + /// static class. + /// + public ILogger? Logger { get; set; } - /// - /// Include the full URL query string in the RequestPath property - /// that is attached to request log events. The default is false. - /// - public bool IncludeQueryInRequestPath { get; set; } + /// + /// Include the full URL query string in the RequestPath property + /// that is attached to request log events. The default is false. + /// + public bool IncludeQueryInRequestPath { get; set; } - /// - /// A function to specify the values of the MessageTemplateProperties. - /// - public Func> GetMessageTemplateProperties { get; set; } + /// + /// A function to specify the values of the MessageTemplateProperties. + /// + public Func> GetMessageTemplateProperties { get; set; } - /// - /// Constructor - /// - public RequestLoggingOptions() - { - GetLevel = DefaultGetLevel; - MessageTemplate = DefaultRequestCompletionMessageTemplate; - GetMessageTemplateProperties = DefaultGetMessageTemplateProperties; - } + /// + /// Constructor + /// + public RequestLoggingOptions() + { + GetLevel = DefaultGetLevel; + MessageTemplate = DefaultRequestCompletionMessageTemplate; + GetMessageTemplateProperties = DefaultGetMessageTemplateProperties; } -} +} \ No newline at end of file diff --git a/src/Serilog.AspNetCore/AspNetCore/SerilogLoggerFactory.cs b/src/Serilog.AspNetCore/AspNetCore/SerilogLoggerFactory.cs index b468dea..01b3686 100644 --- a/src/Serilog.AspNetCore/AspNetCore/SerilogLoggerFactory.cs +++ b/src/Serilog.AspNetCore/AspNetCore/SerilogLoggerFactory.cs @@ -12,62 +12,60 @@ // See the License for the specific language governing permissions and // limitations under the License. -using System; using System.ComponentModel; using Microsoft.Extensions.Logging; using Serilog.Debugging; using Serilog.Extensions.Logging; -namespace Serilog.AspNetCore +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; + /// - /// Implements so that we can inject Serilog Logger. + /// Initializes a new instance of the class. /// - [Obsolete("Replaced with Serilog.Extensions.Logging.SerilogLoggerFactory")] - [EditorBrowsable(EditorBrowsableState.Never)] - public class SerilogLoggerFactory : ILoggerFactory + /// 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) { - 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 SerilogLoggerProvider(logger, dispose); - } + _provider = new(logger, dispose); + } - /// - /// Disposes the provider. - /// - public void Dispose() - { - _provider.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); - } + /// + /// 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); - } + /// + /// 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/Serilog.AspNetCore.csproj b/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj index 1635423..d468a42 100644 --- a/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj +++ b/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj @@ -5,11 +5,7 @@ 6.1.0 Microsoft;Serilog Contributors netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0 - true true - ../../assets/Serilog.snk - true - true serilog;aspnet;aspnetcore icon.png https://github.com/serilog/serilog-aspnetcore diff --git a/src/Serilog.AspNetCore/SerilogApplicationBuilderExtensions.cs b/src/Serilog.AspNetCore/SerilogApplicationBuilderExtensions.cs index b163fc7..6427a6d 100644 --- a/src/Serilog.AspNetCore/SerilogApplicationBuilderExtensions.cs +++ b/src/Serilog.AspNetCore/SerilogApplicationBuilderExtensions.cs @@ -12,67 +12,64 @@ // See the License for the specific language governing permissions and // limitations under the License. -using System; - using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Serilog.AspNetCore; -namespace Serilog +namespace Serilog; + +/// +/// Extends with methods for configuring Serilog features. +/// +public static class SerilogApplicationBuilderExtensions { /// - /// Extends with methods for configuring Serilog features. + /// Adds middleware for streamlined request logging. Instead of writing HTTP request information + /// like method, path, timing, status code and exception details + /// in several events, this middleware collects information during the request (including from + /// ), and writes a single event at request completion. Add this + /// in Startup.cs before any handlers whose activities should be logged. /// - public static class SerilogApplicationBuilderExtensions - { - /// - /// Adds middleware for streamlined request logging. Instead of writing HTTP request information - /// like method, path, timing, status code and exception details - /// in several events, this middleware collects information during the request (including from - /// ), and writes a single event at request completion. Add this - /// in Startup.cs before any handlers whose activities should be logged. - /// - /// The application builder. - /// The message template to use when logging request completion - /// events. The default is - /// "HTTP {RequestMethod} {RequestPath} responded {StatusCode} in {Elapsed:0.0000} ms". The - /// template can contain any of the placeholders from the default template, names of properties - /// added by ASP.NET Core, and names of properties added to the . - /// - /// The application builder. - public static IApplicationBuilder UseSerilogRequestLogging( - this IApplicationBuilder app, - string messageTemplate) - => app.UseSerilogRequestLogging(opts => opts.MessageTemplate = messageTemplate); + /// The application builder. + /// The message template to use when logging request completion + /// events. The default is + /// "HTTP {RequestMethod} {RequestPath} responded {StatusCode} in {Elapsed:0.0000} ms". The + /// template can contain any of the placeholders from the default template, names of properties + /// added by ASP.NET Core, and names of properties added to the . + /// + /// The application builder. + public static IApplicationBuilder UseSerilogRequestLogging( + this IApplicationBuilder app, + string messageTemplate) + => app.UseSerilogRequestLogging(opts => opts.MessageTemplate = messageTemplate); - /// - /// Adds middleware for streamlined request logging. Instead of writing HTTP request information - /// like method, path, timing, status code and exception details - /// in several events, this middleware collects information during the request (including from - /// ), and writes a single event at request completion. Add this - /// in Startup.cs before any handlers whose activities should be logged. - /// - /// The application builder. - /// A to configure the provided . - /// The application builder. - public static IApplicationBuilder UseSerilogRequestLogging( - this IApplicationBuilder app, - Action configureOptions = null) - { - if (app == null) throw new ArgumentNullException(nameof(app)); + /// + /// Adds middleware for streamlined request logging. Instead of writing HTTP request information + /// like method, path, timing, status code and exception details + /// in several events, this middleware collects information during the request (including from + /// ), and writes a single event at request completion. Add this + /// in Startup.cs before any handlers whose activities should be logged. + /// + /// The application builder. + /// A to configure the provided . + /// The application builder. + public static IApplicationBuilder UseSerilogRequestLogging( + this IApplicationBuilder app, + Action? configureOptions = null) + { + if (app == null) throw new ArgumentNullException(nameof(app)); - var opts = app.ApplicationServices.GetService>()?.Value ?? new RequestLoggingOptions(); + var opts = app.ApplicationServices.GetService>()?.Value ?? new RequestLoggingOptions(); - configureOptions?.Invoke(opts); + configureOptions?.Invoke(opts); - if (opts.MessageTemplate == null) - throw new ArgumentException($"{nameof(opts.MessageTemplate)} cannot be null."); - if (opts.GetLevel == null) - throw new ArgumentException($"{nameof(opts.GetLevel)} cannot be null."); + if (opts.MessageTemplate == null) + throw new ArgumentException($"{nameof(opts.MessageTemplate)} cannot be null."); + if (opts.GetLevel == null) + throw new ArgumentException($"{nameof(opts.GetLevel)} cannot be null."); - return app.UseMiddleware(opts); - } + return app.UseMiddleware(opts); } } \ No newline at end of file diff --git a/src/Serilog.AspNetCore/SerilogWebHostBuilderExtensions.cs b/src/Serilog.AspNetCore/SerilogWebHostBuilderExtensions.cs index 7de1bef..a144808 100644 --- a/src/Serilog.AspNetCore/SerilogWebHostBuilderExtensions.cs +++ b/src/Serilog.AspNetCore/SerilogWebHostBuilderExtensions.cs @@ -12,154 +12,152 @@ // See the License for the specific language governing permissions and // limitations under the License. -using System; using Microsoft.Extensions.DependencyInjection; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Logging; using Serilog.Extensions.Hosting; using Serilog.Extensions.Logging; -namespace Serilog +namespace Serilog; + +/// +/// Extends with Serilog configuration methods. +/// +public static class SerilogWebHostBuilderExtensions { /// - /// Extends with Serilog configuration methods. + /// Sets Serilog as the logging provider. /// - 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. + /// 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. #if HOSTBUILDER [Obsolete("Prefer UseSerilog() on IHostBuilder")] #endif - public static IWebHostBuilder UseSerilog( - this IWebHostBuilder builder, - ILogger logger = null, - bool dispose = false, - LoggerProviderCollection providers = null) - { - if (builder == null) throw new ArgumentNullException(nameof(builder)); + 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 => + builder.ConfigureServices(collection => + { + if (providers != null) { - if (providers != null) + collection.AddSingleton(services => { - collection.AddSingleton(services => - { - var factory = new SerilogLoggerFactory(logger, dispose, providers); + var factory = new SerilogLoggerFactory(logger, dispose, providers); - foreach (var provider in services.GetServices()) - factory.AddProvider(provider); + foreach (var provider in services.GetServices()) + factory.AddProvider(provider); - return factory; - }); - } - else - { - collection.AddSingleton(services => new SerilogLoggerFactory(logger, dispose)); - } + return factory; + }); + } + else + { + collection.AddSingleton(_ => new SerilogLoggerFactory(logger, dispose)); + } - ConfigureServices(collection, logger); - }); + ConfigureServices(collection, logger); + }); - return builder; - } + 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. + /// 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. #if HOSTBUILDER [Obsolete("Prefer UseSerilog() on IHostBuilder")] #endif - public static IWebHostBuilder UseSerilog( - this IWebHostBuilder builder, - Action configureLogger, - bool preserveStaticLogger = false, - bool writeToProviders = false) + 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) => { - if (builder == null) throw new ArgumentNullException(nameof(builder)); - if (configureLogger == null) throw new ArgumentNullException(nameof(configureLogger)); + var loggerConfiguration = new LoggerConfiguration(); - builder.ConfigureServices((context, collection) => + LoggerProviderCollection? loggerProviders = null; + if (writeToProviders) { - var loggerConfiguration = new LoggerConfiguration(); + loggerProviders = new(); + loggerConfiguration.WriteTo.Providers(loggerProviders); + } - LoggerProviderCollection loggerProviders = null; - if (writeToProviders) - { - loggerProviders = new LoggerProviderCollection(); - loggerConfiguration.WriteTo.Providers(loggerProviders); - } + configureLogger(context, loggerConfiguration); + var logger = loggerConfiguration.CreateLogger(); - configureLogger(context, loggerConfiguration); - var logger = loggerConfiguration.CreateLogger(); - - ILogger registeredLogger = null; - if (preserveStaticLogger) - { - registeredLogger = logger; - } - else + 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) { - // 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; + foreach (var provider in services.GetServices()) + factory.AddProvider(provider); } - collection.AddSingleton(services => - { - var factory = new SerilogLoggerFactory(registeredLogger, true, loggerProviders); + return factory; + }); - if (writeToProviders) - { - foreach (var provider in services.GetServices()) - factory.AddProvider(provider); - } + ConfigureServices(collection, logger); + }); + return builder; + } - return factory; - }); + static void ConfigureServices(IServiceCollection collection, ILogger? logger) + { + if (collection == null) throw new ArgumentNullException(nameof(collection)); - ConfigureServices(collection, logger); - }); - return builder; - } - - static void ConfigureServices(IServiceCollection collection, ILogger logger) + if (logger != null) { - 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); - } + // This won't (and shouldn't) take ownership of the logger. + collection.AddSingleton(logger); + } - // Registered to provide two services... - var diagnosticContext = new DiagnosticContext(logger); + // Registered to provide two services... + var diagnosticContext = new DiagnosticContext(logger); - // Consumed by e.g. middleware - collection.AddSingleton(diagnosticContext); + // Consumed by e.g. middleware + collection.AddSingleton(diagnosticContext); - // Consumed by user code - 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 c229883..240288c 100644 --- a/test/Serilog.AspNetCore.Tests/Serilog.AspNetCore.Tests.csproj +++ b/test/Serilog.AspNetCore.Tests/Serilog.AspNetCore.Tests.csproj @@ -1,11 +1,7 @@  - netcoreapp3.1;net5.0 - Serilog.AspNetCore.Tests - ../../assets/Serilog.snk - true - true + netcoreapp3.1;net6.0 true @@ -23,7 +19,7 @@ - + diff --git a/test/Serilog.AspNetCore.Tests/SerilogWebHostBuilderExtensionsTests.cs b/test/Serilog.AspNetCore.Tests/SerilogWebHostBuilderExtensionsTests.cs index 09f6c1b..842c2d1 100644 --- a/test/Serilog.AspNetCore.Tests/SerilogWebHostBuilderExtensionsTests.cs +++ b/test/Serilog.AspNetCore.Tests/SerilogWebHostBuilderExtensionsTests.cs @@ -1,9 +1,6 @@ // 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; -using System.Linq; -using System.Threading.Tasks; using Xunit; using Microsoft.Extensions.DependencyInjection; using Microsoft.AspNetCore.Hosting; @@ -13,171 +10,173 @@ using Serilog.Filters; using Serilog.AspNetCore.Tests.Support; using Serilog.Events; -using System.Net.Http; // Newer frameworks provide IHostBuilder #pragma warning disable CS0618 -namespace Serilog.AspNetCore.Tests +namespace Serilog.AspNetCore.Tests; + +public class SerilogWebHostBuilderExtensionsTests : IClassFixture { - public class SerilogWebHostBuilderExtensionsTests : IClassFixture + readonly SerilogWebApplicationFactory _web; + + public SerilogWebHostBuilderExtensionsTests(SerilogWebApplicationFactory web) { - readonly SerilogWebApplicationFactory _web; + _web = web; + } - public SerilogWebHostBuilderExtensionsTests(SerilogWebApplicationFactory web) + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task DisposeShouldBeHandled(bool dispose) + { + var logger = new DisposeTrackingLogger(); + using (var web = Setup(logger, dispose)) { - _web = web; + await web.CreateClient().GetAsync("/"); } - [Theory] - [InlineData(true)] - [InlineData(false)] - public async Task DisposeShouldBeHandled(bool dispose) - { - var logger = new DisposeTrackingLogger(); - using (var web = Setup(logger, dispose)) - { - await web.CreateClient().GetAsync("/"); - } - - Assert.Equal(dispose, logger.IsDisposed); - } + Assert.Equal(dispose, logger.IsDisposed); + } - [Fact] - public async Task RequestLoggingMiddlewareShouldEnrich() + [Fact] + public async Task RequestLoggingMiddlewareShouldEnrich() + { + var (sink, web) = Setup(options => { - var (sink, web) = Setup(options => + options.EnrichDiagnosticContext += (diagnosticContext, _) => { - options.EnrichDiagnosticContext += (diagnosticContext, httpContext) => - { - diagnosticContext.Set("SomeInteger", 42); - }; - }); + diagnosticContext.Set("SomeInteger", 42); + }; + }); - await web.CreateClient().GetAsync("/resource"); + await web.CreateClient().GetAsync("/resource"); - Assert.NotEmpty(sink.Writes); + Assert.NotEmpty(sink.Writes); - var completionEvent = sink.Writes.First(logEvent => Matching.FromSource()(logEvent)); + var completionEvent = sink.Writes.First(logEvent => Matching.FromSource()(logEvent)); - Assert.Equal(42, completionEvent.Properties["SomeInteger"].LiteralValue()); - Assert.Equal("string", completionEvent.Properties["SomeString"].LiteralValue()); - Assert.Equal("/resource", completionEvent.Properties["RequestPath"].LiteralValue()); - Assert.Equal(200, completionEvent.Properties["StatusCode"].LiteralValue()); - Assert.Equal("GET", completionEvent.Properties["RequestMethod"].LiteralValue()); - Assert.True(completionEvent.Properties.ContainsKey("Elapsed")); - } + Assert.Equal(42, completionEvent.Properties["SomeInteger"].LiteralValue()); + Assert.Equal("string", completionEvent.Properties["SomeString"].LiteralValue()); + Assert.Equal("/resource", completionEvent.Properties["RequestPath"].LiteralValue()); + Assert.Equal(200, completionEvent.Properties["StatusCode"].LiteralValue()); + Assert.Equal("GET", completionEvent.Properties["RequestMethod"].LiteralValue()); + Assert.True(completionEvent.Properties.ContainsKey("Elapsed")); + } - [Fact] - public async Task RequestLoggingMiddlewareShouldEnrichWithCustomisedProperties() + [Fact] + public async Task RequestLoggingMiddlewareShouldEnrichWithCustomisedProperties() + { + var (sink, web) = Setup(options => { - var (sink, web) = Setup(options => - { - options.MessageTemplate = "HTTP {RequestMethod} responded {Status} in {ElapsedMilliseconds:0.0000} ms"; - options.GetMessageTemplateProperties = (ctx, path, elapsedMs, status) => - new[] - { - new LogEventProperty("RequestMethod", new ScalarValue(ctx.Request.Method)), - new LogEventProperty("Status", new ScalarValue(status)), - new LogEventProperty("ElapsedMilliseconds", new ScalarValue(elapsedMs)) - }; - }); + options.MessageTemplate = "HTTP {RequestMethod} responded {Status} in {ElapsedMilliseconds:0.0000} ms"; + options.GetMessageTemplateProperties = (ctx, _, elapsedMs, status) => + new[] + { + new LogEventProperty("RequestMethod", new ScalarValue(ctx.Request.Method)), + new LogEventProperty("Status", new ScalarValue(status)), + new LogEventProperty("ElapsedMilliseconds", new ScalarValue(elapsedMs)) + }; + }); - await web.CreateClient().GetAsync("/resource"); + await web.CreateClient().GetAsync("/resource"); - Assert.NotEmpty(sink.Writes); + Assert.NotEmpty(sink.Writes); - var completionEvent = sink.Writes.First(logEvent => Matching.FromSource()(logEvent)); + var completionEvent = sink.Writes.First(logEvent => Matching.FromSource()(logEvent)); - Assert.Equal("string", completionEvent.Properties["SomeString"].LiteralValue()); - Assert.Equal(200, completionEvent.Properties["Status"].LiteralValue()); - Assert.Equal("GET", completionEvent.Properties["RequestMethod"].LiteralValue()); - Assert.True(completionEvent.Properties.ContainsKey("ElapsedMilliseconds")); - Assert.False(completionEvent.Properties.ContainsKey("Elapsed")); - } + Assert.Equal("string", completionEvent.Properties["SomeString"].LiteralValue()); + Assert.Equal(200, completionEvent.Properties["Status"].LiteralValue()); + Assert.Equal("GET", completionEvent.Properties["RequestMethod"].LiteralValue()); + Assert.True(completionEvent.Properties.ContainsKey("ElapsedMilliseconds")); + Assert.False(completionEvent.Properties.ContainsKey("Elapsed")); + } - [Fact] - public async Task RequestLoggingMiddlewareShouldEnrichWithCollectedExceptionIfNoUnhandledException() + [Fact] + public async Task RequestLoggingMiddlewareShouldEnrichWithCollectedExceptionIfNoUnhandledException() + { + var diagnosticContextException = new Exception("Exception set in diagnostic context"); + var (sink, web) = Setup(options => { - var diagnosticContextException = new Exception("Exception set in diagnostic context"); - var (sink, web) = Setup(options => + options.EnrichDiagnosticContext += (diagnosticContext, _) => { - options.EnrichDiagnosticContext += (diagnosticContext, _) => - { - diagnosticContext.SetException(diagnosticContextException); - }; - }); + diagnosticContext.SetException(diagnosticContextException); + }; + }); - await web.CreateClient().GetAsync("/resource"); + await web.CreateClient().GetAsync("/resource"); - var completionEvent = sink.Writes.First(logEvent => Matching.FromSource()(logEvent)); + var completionEvent = sink.Writes.First(logEvent => Matching.FromSource()(logEvent)); - Assert.Same(diagnosticContextException, completionEvent.Exception); - } + Assert.Same(diagnosticContextException, completionEvent.Exception); + } - [Theory] - [InlineData(false)] - [InlineData(true)] - public async Task RequestLoggingMiddlewareShouldEnrichWithUnhandledExceptionEvenIfExceptionIsSetInDiagnosticContext(bool setExceptionInDiagnosticContext) + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task RequestLoggingMiddlewareShouldEnrichWithUnhandledExceptionEvenIfExceptionIsSetInDiagnosticContext(bool setExceptionInDiagnosticContext) + { + var diagnosticContextException = new Exception("Exception set in diagnostic context"); + var unhandledException = new Exception("Unhandled exception thrown in API action"); + var (sink, web) = Setup(options => { - var diagnosticContextException = new Exception("Exception set in diagnostic context"); - var unhandledException = new Exception("Unhandled exception thrown in API action"); - var (sink, web) = Setup(options => + options.EnrichDiagnosticContext += (diagnosticContext, _) => { - options.EnrichDiagnosticContext += (diagnosticContext, _) => - { - if (setExceptionInDiagnosticContext) - diagnosticContext.SetException(diagnosticContextException); - }; - }, actionCallback: _ => throw unhandledException); + if (setExceptionInDiagnosticContext) + diagnosticContext.SetException(diagnosticContextException); + }; + }, actionCallback: _ => throw unhandledException); - Func act = () => web.CreateClient().GetAsync("/resource"); + Func act = () => web.CreateClient().GetAsync("/resource"); - Exception thrownException = await Assert.ThrowsAsync(act); - var completionEvent = sink.Writes.First(logEvent => Matching.FromSource()(logEvent)); - Assert.Same(unhandledException, completionEvent.Exception); - Assert.Same(unhandledException, thrownException); - } + var thrownException = await Assert.ThrowsAsync(act); + var completionEvent = sink.Writes.First(logEvent => Matching.FromSource()(logEvent)); + Assert.Same(unhandledException, completionEvent.Exception); + Assert.Same(unhandledException, thrownException); + } - WebApplicationFactory Setup(ILogger logger, bool dispose, Action configureOptions = null, - Action actionCallback = null) - { - var web = _web.WithWebHostBuilder( - builder => builder - .ConfigureServices(sc => sc.Configure(options => + WebApplicationFactory Setup( + ILogger logger, + bool dispose, + Action? configureOptions = null, + Action? actionCallback = null) + { + var web = _web.WithWebHostBuilder( + builder => builder + .ConfigureServices(sc => sc.Configure(options => + { + options.Logger = logger; + options.EnrichDiagnosticContext += (diagnosticContext, _) => { - options.Logger = logger; - options.EnrichDiagnosticContext += (diagnosticContext, httpContext) => - { - diagnosticContext.Set("SomeString", "string"); - }; - })) - .Configure(app => + diagnosticContext.Set("SomeString", "string"); + }; + })) + .Configure(app => + { + app.UseSerilogRequestLogging(configureOptions); + app.Run(ctx => { - app.UseSerilogRequestLogging(configureOptions); - app.Run(ctx => - { - actionCallback?.Invoke(ctx); - return Task.CompletedTask; - }); // 200 OK - }) - .UseSerilog(logger, dispose)); - - return web; - } + actionCallback?.Invoke(ctx); + return Task.CompletedTask; + }); // 200 OK + }) + .UseSerilog(logger, dispose)); - (SerilogSink, WebApplicationFactory) Setup(Action configureOptions = null, - Action actionCallback = null) - { - var sink = new SerilogSink(); - var logger = new LoggerConfiguration() - .Enrich.FromLogContext() - .WriteTo.Sink(sink) - .CreateLogger(); + return web; + } - var web = Setup(logger, true, configureOptions, actionCallback); + (SerilogSink, WebApplicationFactory) Setup( + Action? configureOptions = null, + Action? actionCallback = null) + { + var sink = new SerilogSink(); + var logger = new LoggerConfiguration() + .Enrich.FromLogContext() + .WriteTo.Sink(sink) + .CreateLogger(); - return (sink, web); - } + var web = Setup(logger, true, configureOptions, actionCallback); + + return (sink, web); } -} +} \ No newline at end of file diff --git a/test/Serilog.AspNetCore.Tests/Support/DisposeTrackingLogger.cs b/test/Serilog.AspNetCore.Tests/Support/DisposeTrackingLogger.cs index 70d3ef0..a441089 100644 --- a/test/Serilog.AspNetCore.Tests/Support/DisposeTrackingLogger.cs +++ b/test/Serilog.AspNetCore.Tests/Support/DisposeTrackingLogger.cs @@ -1,354 +1,354 @@ -using System; -using System.Collections.Generic; -using Serilog.Core; +using Serilog.Core; using Serilog.Events; -namespace Serilog.AspNetCore.Tests.Support +namespace Serilog.AspNetCore.Tests.Support; + +public class DisposeTrackingLogger : ILogger, IDisposable { - public class DisposeTrackingLogger : ILogger, IDisposable - { - public bool IsDisposed { get; set; } - - public ILogger ForContext(ILogEventEnricher enricher) - { - return new LoggerConfiguration().CreateLogger(); - } - - public ILogger ForContext(IEnumerable enrichers) - { - return new LoggerConfiguration().CreateLogger(); - } - - public ILogger ForContext(string propertyName, object value, bool destructureObjects = false) - { - return new LoggerConfiguration().CreateLogger(); - } - - public ILogger ForContext() - { - return new LoggerConfiguration().CreateLogger(); - } - - public ILogger ForContext(Type source) - { - return new LoggerConfiguration().CreateLogger(); - } - - public void Write(LogEvent logEvent) - { - } - - public void Write(LogEventLevel level, string messageTemplate) - { - } - - public void Write(LogEventLevel level, string messageTemplate, T propertyValue) - { - } - - public void Write(LogEventLevel level, string messageTemplate, T0 propertyValue0, T1 propertyValue1) - { - } - - public void Write(LogEventLevel level, string messageTemplate, T0 propertyValue0, T1 propertyValue1, - T2 propertyValue2) - { - } - - public void Write(LogEventLevel level, string messageTemplate, params object[] propertyValues) - { - } - - public void Write(LogEventLevel level, Exception exception, string messageTemplate) - { - } - - public void Write(LogEventLevel level, Exception exception, string messageTemplate, T propertyValue) - { - } - - public void Write(LogEventLevel level, Exception exception, string messageTemplate, T0 propertyValue0, - T1 propertyValue1) - { - } - - public void Write(LogEventLevel level, Exception exception, string messageTemplate, T0 propertyValue0, - T1 propertyValue1, T2 propertyValue2) - { - } - - public void Write(LogEventLevel level, Exception exception, string messageTemplate, params object[] propertyValues) - { - } - - public bool IsEnabled(LogEventLevel level) - { - return false; - } - - public void Verbose(string messageTemplate) - { - } - - public void Verbose(string messageTemplate, T propertyValue) - { - } - - public void Verbose(string messageTemplate, T0 propertyValue0, T1 propertyValue1) - { - } - - public void Verbose(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) - { - } - - public void Verbose(string messageTemplate, params object[] propertyValues) - { - } - - public void Verbose(Exception exception, string messageTemplate) - { - } - - public void Verbose(Exception exception, string messageTemplate, T propertyValue) - { - } - - public void Verbose(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1) - { - } - - public void Verbose(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1, - T2 propertyValue2) - { - } - - public void Verbose(Exception exception, string messageTemplate, params object[] propertyValues) - { - } - - public void Debug(string messageTemplate) - { - } - - public void Debug(string messageTemplate, T propertyValue) - { - } - - public void Debug(string messageTemplate, T0 propertyValue0, T1 propertyValue1) - { - } - - public void Debug(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) - { - } - - public void Debug(string messageTemplate, params object[] propertyValues) - { - } - - public void Debug(Exception exception, string messageTemplate) - { - } - - public void Debug(Exception exception, string messageTemplate, T propertyValue) - { - } - - public void Debug(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1) - { - } - - public void Debug(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1, - T2 propertyValue2) - { - } - - public void Debug(Exception exception, string messageTemplate, params object[] propertyValues) - { - } - - public void Information(string messageTemplate) - { - } - - public void Information(string messageTemplate, T propertyValue) - { - } - - public void Information(string messageTemplate, T0 propertyValue0, T1 propertyValue1) - { - } - - public void Information(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) - { - } - - public void Information(string messageTemplate, params object[] propertyValues) - { - } - - public void Information(Exception exception, string messageTemplate) - { - } - - public void Information(Exception exception, string messageTemplate, T propertyValue) - { - } - - public void Information(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1) - { - } - - public void Information(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1, - T2 propertyValue2) - { - } - - public void Information(Exception exception, string messageTemplate, params object[] propertyValues) - { - } - - public void Warning(string messageTemplate) - { - } - - public void Warning(string messageTemplate, T propertyValue) - { - } - - public void Warning(string messageTemplate, T0 propertyValue0, T1 propertyValue1) - { - } - - public void Warning(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) - { - } - - public void Warning(string messageTemplate, params object[] propertyValues) - { - } - - public void Warning(Exception exception, string messageTemplate) - { - } - - public void Warning(Exception exception, string messageTemplate, T propertyValue) - { - } - - public void Warning(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1) - { - } - - public void Warning(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1, - T2 propertyValue2) - { - } - - public void Warning(Exception exception, string messageTemplate, params object[] propertyValues) - { - } - - public void Error(string messageTemplate) - { - } - - public void Error(string messageTemplate, T propertyValue) - { - } - - public void Error(string messageTemplate, T0 propertyValue0, T1 propertyValue1) - { - } - - public void Error(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) - { - } - - public void Error(string messageTemplate, params object[] propertyValues) - { - } - - public void Error(Exception exception, string messageTemplate) - { - } - - public void Error(Exception exception, string messageTemplate, T propertyValue) - { - } - - public void Error(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1) - { - } - - public void Error(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1, - T2 propertyValue2) - { - } - - public void Error(Exception exception, string messageTemplate, params object[] propertyValues) - { - } - - public void Fatal(string messageTemplate) - { - } - - public void Fatal(string messageTemplate, T propertyValue) - { - } - - public void Fatal(string messageTemplate, T0 propertyValue0, T1 propertyValue1) - { - } - - public void Fatal(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) - { - } - - public void Fatal(string messageTemplate, params object[] propertyValues) - { - } - - public void Fatal(Exception exception, string messageTemplate) - { - } - - public void Fatal(Exception exception, string messageTemplate, T propertyValue) - { - } - - public void Fatal(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1) - { - } - - public void Fatal(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1, - T2 propertyValue2) - { - } - - public void Fatal(Exception exception, string messageTemplate, params object[] propertyValues) - { - } - - public bool BindMessageTemplate(string messageTemplate, object[] propertyValues, out MessageTemplate parsedTemplate, - out IEnumerable boundProperties) - { - parsedTemplate = null; - boundProperties = null; - return false; - } - - public bool BindProperty(string propertyName, object value, bool destructureObjects, out LogEventProperty property) - { - property = null; - return false; - } - - public void Dispose() - { - IsDisposed = true; - } - } -} + public bool IsDisposed { get; set; } + + public ILogger ForContext(ILogEventEnricher enricher) + { + return new LoggerConfiguration().CreateLogger(); + } + + public ILogger ForContext(IEnumerable enrichers) + { + return new LoggerConfiguration().CreateLogger(); + } + + public ILogger ForContext(string propertyName, object value, bool destructureObjects = false) + { + return new LoggerConfiguration().CreateLogger(); + } + + public ILogger ForContext() + { + return new LoggerConfiguration().CreateLogger(); + } + + public ILogger ForContext(Type source) + { + return new LoggerConfiguration().CreateLogger(); + } + + public void Write(LogEvent logEvent) + { + } + + public void Write(LogEventLevel level, string messageTemplate) + { + } + + public void Write(LogEventLevel level, string messageTemplate, T propertyValue) + { + } + + public void Write(LogEventLevel level, string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Write(LogEventLevel level, string messageTemplate, T0 propertyValue0, T1 propertyValue1, + T2 propertyValue2) + { + } + + public void Write(LogEventLevel level, string messageTemplate, params object[] propertyValues) + { + } + + public void Write(LogEventLevel level, Exception exception, string messageTemplate) + { + } + + public void Write(LogEventLevel level, Exception exception, string messageTemplate, T propertyValue) + { + } + + public void Write(LogEventLevel level, Exception exception, string messageTemplate, T0 propertyValue0, + T1 propertyValue1) + { + } + + public void Write(LogEventLevel level, Exception exception, string messageTemplate, T0 propertyValue0, + T1 propertyValue1, T2 propertyValue2) + { + } + + public void Write(LogEventLevel level, Exception exception, string messageTemplate, params object[] propertyValues) + { + } + + public bool IsEnabled(LogEventLevel level) + { + return false; + } + + public void Verbose(string messageTemplate) + { + } + + public void Verbose(string messageTemplate, T propertyValue) + { + } + + public void Verbose(string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Verbose(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) + { + } + + public void Verbose(string messageTemplate, params object[] propertyValues) + { + } + + public void Verbose(Exception exception, string messageTemplate) + { + } + + public void Verbose(Exception exception, string messageTemplate, T propertyValue) + { + } + + public void Verbose(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Verbose(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1, + T2 propertyValue2) + { + } + + public void Verbose(Exception exception, string messageTemplate, params object[] propertyValues) + { + } + + public void Debug(string messageTemplate) + { + } + + public void Debug(string messageTemplate, T propertyValue) + { + } + + public void Debug(string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Debug(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) + { + } + + public void Debug(string messageTemplate, params object[] propertyValues) + { + } + + public void Debug(Exception exception, string messageTemplate) + { + } + + public void Debug(Exception exception, string messageTemplate, T propertyValue) + { + } + + public void Debug(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Debug(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1, + T2 propertyValue2) + { + } + + public void Debug(Exception exception, string messageTemplate, params object[] propertyValues) + { + } + + public void Information(string messageTemplate) + { + } + + public void Information(string messageTemplate, T propertyValue) + { + } + + public void Information(string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Information(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) + { + } + + public void Information(string messageTemplate, params object[] propertyValues) + { + } + + public void Information(Exception exception, string messageTemplate) + { + } + + public void Information(Exception exception, string messageTemplate, T propertyValue) + { + } + + public void Information(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Information(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1, + T2 propertyValue2) + { + } + + public void Information(Exception exception, string messageTemplate, params object[] propertyValues) + { + } + + public void Warning(string messageTemplate) + { + } + + public void Warning(string messageTemplate, T propertyValue) + { + } + + public void Warning(string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Warning(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) + { + } + + public void Warning(string messageTemplate, params object[] propertyValues) + { + } + + public void Warning(Exception exception, string messageTemplate) + { + } + + public void Warning(Exception exception, string messageTemplate, T propertyValue) + { + } + + public void Warning(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Warning(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1, + T2 propertyValue2) + { + } + + public void Warning(Exception exception, string messageTemplate, params object[] propertyValues) + { + } + + public void Error(string messageTemplate) + { + } + + public void Error(string messageTemplate, T propertyValue) + { + } + + public void Error(string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Error(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) + { + } + + public void Error(string messageTemplate, params object[] propertyValues) + { + } + + public void Error(Exception exception, string messageTemplate) + { + } + + public void Error(Exception exception, string messageTemplate, T propertyValue) + { + } + + public void Error(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Error(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1, + T2 propertyValue2) + { + } + + public void Error(Exception exception, string messageTemplate, params object[] propertyValues) + { + } + + public void Fatal(string messageTemplate) + { + } + + public void Fatal(string messageTemplate, T propertyValue) + { + } + + public void Fatal(string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Fatal(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) + { + } + + public void Fatal(string messageTemplate, params object[] propertyValues) + { + } + + public void Fatal(Exception exception, string messageTemplate) + { + } + + public void Fatal(Exception exception, string messageTemplate, T propertyValue) + { + } + + public void Fatal(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Fatal(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1, + T2 propertyValue2) + { + } + + public void Fatal(Exception exception, string messageTemplate, params object[] propertyValues) + { + } + + public bool BindMessageTemplate( + string messageTemplate, + object[] propertyValues, + out MessageTemplate? parsedTemplate, + out IEnumerable? boundProperties) + { + parsedTemplate = null; + boundProperties = null; + return false; + } + + public bool BindProperty(string propertyName, object value, bool destructureObjects, out LogEventProperty? property) + { + property = null; + return false; + } + + public void Dispose() + { + IsDisposed = true; + } +} \ No newline at end of file diff --git a/test/Serilog.AspNetCore.Tests/Support/Extensions.cs b/test/Serilog.AspNetCore.Tests/Support/Extensions.cs index 54e6a1f..78ac692 100644 --- a/test/Serilog.AspNetCore.Tests/Support/Extensions.cs +++ b/test/Serilog.AspNetCore.Tests/Support/Extensions.cs @@ -1,12 +1,11 @@ using Serilog.Events; -namespace Serilog.AspNetCore.Tests.Support +namespace Serilog.AspNetCore.Tests.Support; + +public static class Extensions { - public static class Extensions + public static object LiteralValue(this LogEventPropertyValue @this) { - public static object LiteralValue(this LogEventPropertyValue @this) - { - return ((ScalarValue)@this).Value; - } + return ((ScalarValue)@this).Value; } -} +} \ No newline at end of file diff --git a/test/Serilog.AspNetCore.Tests/Support/SerilogSink.cs b/test/Serilog.AspNetCore.Tests/Support/SerilogSink.cs index 5af3d36..62e5966 100644 --- a/test/Serilog.AspNetCore.Tests/Support/SerilogSink.cs +++ b/test/Serilog.AspNetCore.Tests/Support/SerilogSink.cs @@ -1,19 +1,17 @@ // 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.Collections.Generic; using Serilog.Core; using Serilog.Events; -namespace Serilog.AspNetCore.Tests.Support +namespace Serilog.AspNetCore.Tests.Support; + +public class SerilogSink : ILogEventSink { - public class SerilogSink : ILogEventSink - { - public List Writes { get; set; } = new List(); + public List Writes { get; set; } = new(); - 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/Serilog.AspNetCore.Tests/Support/SerilogWebApplicationFactory.cs b/test/Serilog.AspNetCore.Tests/Support/SerilogWebApplicationFactory.cs index ac6a881..f4fe961 100644 --- a/test/Serilog.AspNetCore.Tests/Support/SerilogWebApplicationFactory.cs +++ b/test/Serilog.AspNetCore.Tests/Support/SerilogWebApplicationFactory.cs @@ -1,14 +1,13 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; -namespace Serilog.AspNetCore.Tests.Support -{ - // ReSharper disable once ClassNeverInstantiated.Global - public class SerilogWebApplicationFactory : WebApplicationFactory - { - protected override IWebHostBuilder CreateWebHostBuilder() => new WebHostBuilder().UseStartup(); - protected override void ConfigureWebHost(IWebHostBuilder builder) => builder.UseContentRoot("."); - } +namespace Serilog.AspNetCore.Tests.Support; - public class TestStartup { } +// ReSharper disable once ClassNeverInstantiated.Global +public class SerilogWebApplicationFactory : WebApplicationFactory +{ + protected override IWebHostBuilder CreateWebHostBuilder() => new WebHostBuilder().UseStartup(); + protected override void ConfigureWebHost(IWebHostBuilder builder) => builder.UseContentRoot("."); } + +public class TestStartup { } \ No newline at end of file From 56fefca302c521285f165f2a2d665729fff488c8 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Mon, 28 Nov 2022 10:06:22 +1000 Subject: [PATCH 11/12] Quick README updates to reflect .NET 6+ host configuration --- README.md | 146 ++++++++++++++++++++---------------------------------- 1 file changed, 55 insertions(+), 91 deletions(-) diff --git a/README.md b/README.md index 379d595..2c3e9e8 100644 --- a/README.md +++ b/README.md @@ -18,53 +18,38 @@ dotnet add package Serilog.AspNetCore ```csharp using Serilog; -using Serilog.Events; -public class Program +Log.Logger = new LoggerConfiguration() + .WriteTo.Console() + .CreateLogger(); + +try { - public static int Main(string[] args) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Override("Microsoft", LogEventLevel.Information) - .Enrich.FromLogContext() - .WriteTo.Console() - .CreateLogger(); + Log.Information("Starting web application"); - try - { - Log.Information("Starting web host"); - CreateHostBuilder(args).Build().Run(); - return 0; - } - catch (Exception ex) - { - Log.Fatal(ex, "Host terminated unexpectedly"); - return 1; - } - finally - { - Log.CloseAndFlush(); - } - } -``` + var builder = WebApplication.CreateBuilder(args); + + builder.Host.UseSerilog(); // <-- Add this line + + var app = builder.Build(); -**Then**, add `UseSerilog()` to the Generic Host in `CreateHostBuilder()`. + app.MapGet("/", () => "Hello World!"); -```csharp - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .UseSerilog() // <-- Add this line - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }); + app.Run(); +} +catch (Exception ex) +{ + Log.Fatal(ex, "Application terminated unexpectedly"); +} +finally +{ + Log.CloseAndFlush(); } ``` -**Finally**, clean up by removing the remaining configuration for the default logger: +The `builder.Host.UseSerilog()` call will redirect all log events through your Serilog pipeline. - * Remove the `"Logging"` section from _appsettings.*.json_ files (this can be replaced with [Serilog configuration](https://github.com/serilog/serilog-settings-configuration) as shown in [the _Sample_ project](https://github.com/serilog/serilog-aspnetcore/blob/dev/samples/Sample/Program.cs), if required) - * Remove `UseApplicationInsights()` (this can be replaced with the [Serilog AI sink](https://github.com/serilog/serilog-sinks-applicationinsights), if required) +**Finally**, clean up by removing the remaining configuration for the default logger, including the `"Logging"` section from _appsettings.*.json_ files (this can be replaced with [Serilog configuration](https://github.com/serilog/serilog-settings-configuration) as shown in [the _Sample_ project](https://github.com/serilog/serilog-aspnetcore/blob/dev/samples/Sample/Program.cs), if required). That's it! With the level bumped up a little you will see log output resembling: @@ -118,23 +103,14 @@ To enable the middleware, first change the minimum level for `Microsoft.AspNetCo .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning) ``` -Then, in your application's _Startup.cs_, add the middleware with `UseSerilogRequestLogging()`: +Then, in your application's _Program.cs_, add the middleware with `UseSerilogRequestLogging()`: ```csharp - public void Configure(IApplicationBuilder app, IHostingEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - else - { - app.UseExceptionHandler("/Home/Error"); - } - - app.UseSerilogRequestLogging(); // <-- Add this line - - // Other app configuration + var app = builder.Build(); + + app.UseSerilogRequestLogging(); // <-- Add this line + + // Other app configuration ``` It's important that the `UseSerilogRequestLogging()` call appears _before_ handlers such as MVC. The middleware will not time or log components that appear before it in the pipeline. (This can be utilized to exclude noisy handlers from logging, such as `UseStaticFiles()`, by placing `UseSerilogRequestLogging()` after them.) @@ -204,31 +180,21 @@ To use this technique, first replace the initial `CreateLogger()` call with `Cre using Serilog; using Serilog.Events; -public class Program -{ - public static int Main(string[] args) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Override("Microsoft", LogEventLevel.Information) - .Enrich.FromLogContext() - .WriteTo.Console() - .CreateBootstrapLogger(); // <-- Change this line! +Log.Logger = new LoggerConfiguration() + .MinimumLevel.Override("Microsoft", LogEventLevel.Information) + .Enrich.FromLogContext() + .WriteTo.Console() + .CreateBootstrapLogger(); // <-- Change this line! ``` Then, pass a callback to `UseSerilog()` that creates the final logger: ```csharp - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .UseSerilog((context, services, configuration) => configuration - .ReadFrom.Configuration(context.Configuration) - .ReadFrom.Services(services) - .Enrich.FromLogContext() - .WriteTo.Console()) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }); +builder.Host.UseSerilog((context, services, configuration) => configuration + .ReadFrom.Configuration(context.Configuration) + .ReadFrom.Services(services) + .Enrich.FromLogContext() + .WriteTo.Console()); ``` It's important to note that the final logger **completely replaces** the bootstrap logger: if you want both to log to the console, for instance, you'll need to specify `WriteTo.Console()` in both places, as the example shows. @@ -256,7 +222,7 @@ By default, Serilog ignores providers, since there are usually equivalent Serilo To have Serilog pass events to providers, **using two-stage initialization** as above, pass `writeToProviders: true` in the call to `UseSerilog()`: ```csharp - .UseSerilog( +builder.Host.UseSerilog( (hostingContext, services, loggerConfiguration) => /* snip! */, writeToProviders: true) ``` @@ -276,23 +242,21 @@ To write newline-delimited JSON, pass a `CompactJsonFormatter` or `RenderedCompa The Azure Diagnostic Log Stream ships events from any files in the `D:\home\LogFiles\` folder. To enable this for your app, add a file sink to your `LoggerConfiguration`, taking care to set the `shared` and `flushToDiskInterval` parameters: ```csharp - public static int Main(string[] args) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Debug() - .MinimumLevel.Override("Microsoft", LogEventLevel.Information) - .Enrich.FromLogContext() - .WriteTo.Console() - // Add this line: - .WriteTo.File( - System.IO.Path.Combine(Environment.GetEnvironmentVariable("HOME"), "LogFiles", "Application", "diagnostics.txt"), - rollingInterval: RollingInterval.Day, - fileSizeLimitBytes: 10 * 1024 * 1024, - retainedFileCountLimit: 2, - rollOnFileSizeLimit: true, - shared: true, - flushToDiskInterval: TimeSpan.FromSeconds(1)) - .CreateLogger(); +Log.Logger = new LoggerConfiguration() + .MinimumLevel.Debug() + .MinimumLevel.Override("Microsoft", LogEventLevel.Information) + .Enrich.FromLogContext() + .WriteTo.Console() + // Add this line: + .WriteTo.File( + System.IO.Path.Combine(Environment.GetEnvironmentVariable("HOME"), "LogFiles", "Application", "diagnostics.txt"), + rollingInterval: RollingInterval.Day, + fileSizeLimitBytes: 10 * 1024 * 1024, + retainedFileCountLimit: 2, + rollOnFileSizeLimit: true, + shared: true, + flushToDiskInterval: TimeSpan.FromSeconds(1)) + .CreateLogger(); ``` ### Pushing properties to the `ILogger` From fd7da3a887440b608ffb26528dbee304c8396dae Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Mon, 28 Nov 2022 10:13:08 +1000 Subject: [PATCH 12/12] Update publishing key --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 2d7ff52..fcd8868 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -12,7 +12,7 @@ deploy: - provider: NuGet skip_symbols: true api_key: - secure: U7I8Skf+EcC7PiqvLWpSzPqNhCg03cqDOi4OtAkb+ZelMlQj1YoKDX8r1pdQzy7H + secure: JZt0dILdf7cKm+rCtxfAHFGefHJHUUtt8Imm+J3+LtqTeVKODNZ7nyLjrP+QGBk5 on: branch: /^(main|dev)$/ - provider: GitHub