From 11ae421171da02bfe463d98a84de9d46b203748c Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Fri, 19 Jan 2024 13:30:50 +1000 Subject: [PATCH 1/6] 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 4fa9cee..875f204 100644 --- a/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj +++ b/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj @@ -3,7 +3,7 @@ Serilog support for ASP.NET Core logging - 8.0.1 + 8.0.2 Microsoft;Serilog Contributors net462;netstandard2.0;net6.0;net7.0;net8.0 true From 178f6df1f775124e0d0609da2d7f20977685128d Mon Sep 17 00:00:00 2001 From: RandomBuffer <160499735+RandomBuffer@users.noreply.github.com> Date: Wed, 6 Mar 2024 00:10:06 +1100 Subject: [PATCH 2/6] Fix C# example for PushProperty. Fix C# example for PushProperty. --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f4c2578..600ce17 100644 --- a/README.md +++ b/README.md @@ -276,12 +276,12 @@ using (logger.BeginScope(new Dictionary } ``` -The code above results in the same outcome as if you would push properties in the **ILogger** in Serilog. +The code above results in the same outcome as if you would push properties in the **LogContext** in Serilog. More details can be found in https://github.com/serilog/serilog/wiki/Enrichment#the-logcontext. ```csharp -// Serilog ILogger -using (logger.PushProperty("UserId", "svrooij")) -using (logger.PushProperty("OperationType", "update")) +// Serilog LogContext +using (LogContext.PushProperty("UserId", "svrooij")) +using (LogContext.PushProperty("OperationType", "update")) { // UserId and OperationType are set for all logging events in these brackets } From 656b79ea2d8357c198053d395733e6f9f7b19ed3 Mon Sep 17 00:00:00 2001 From: Ivan Maximov Date: Fri, 22 Mar 2024 20:06:10 +0300 Subject: [PATCH 3/6] Remove excessive dependencies + format --- samples/Sample/Controllers/HomeController.cs | 2 +- samples/Sample/Sample.csproj | 2 +- .../AspNetCore/RequestLoggingMiddleware.cs | 4 ++-- src/Serilog.AspNetCore/AspNetCore/RequestLoggingOptions.cs | 5 ++--- src/Serilog.AspNetCore/Serilog.AspNetCore.csproj | 3 --- .../SerilogWebHostBuilderExtensionsTests.cs | 7 +++---- test/Serilog.AspNetCore.Tests/Support/SerilogSink.cs | 2 +- 7 files changed, 10 insertions(+), 15 deletions(-) diff --git a/samples/Sample/Controllers/HomeController.cs b/samples/Sample/Controllers/HomeController.cs index ec4ab25..8592337 100644 --- a/samples/Sample/Controllers/HomeController.cs +++ b/samples/Sample/Controllers/HomeController.cs @@ -35,6 +35,6 @@ public IActionResult Privacy() [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/Sample.csproj b/samples/Sample/Sample.csproj index c43ac1a..b5a6d67 100644 --- a/samples/Sample/Sample.csproj +++ b/samples/Sample/Sample.csproj @@ -7,7 +7,7 @@ - + diff --git a/src/Serilog.AspNetCore/AspNetCore/RequestLoggingMiddleware.cs b/src/Serilog.AspNetCore/AspNetCore/RequestLoggingMiddleware.cs index a0bd873..e4f50a8 100644 --- a/src/Serilog.AspNetCore/AspNetCore/RequestLoggingMiddleware.cs +++ b/src/Serilog.AspNetCore/AspNetCore/RequestLoggingMiddleware.cs @@ -32,7 +32,7 @@ class RequestLoggingMiddleware readonly Func> _getMessageTemplateProperties; readonly ILogger? _logger; readonly bool _includeQueryInRequestPath; - static readonly LogEventProperty[] NoProperties = Array.Empty(); + static readonly LogEventProperty[] NoProperties = []; public RequestLoggingMiddleware(RequestDelegate next, DiagnosticContext diagnosticContext, RequestLoggingOptions options) { @@ -102,7 +102,7 @@ bool LogCompletion(HttpContext httpContext, DiagnosticContextCollector collector properties, traceId, spanId); - + logger.Write(evt); return false; diff --git a/src/Serilog.AspNetCore/AspNetCore/RequestLoggingOptions.cs b/src/Serilog.AspNetCore/AspNetCore/RequestLoggingOptions.cs index d782e6d..fe9c1ab 100644 --- a/src/Serilog.AspNetCore/AspNetCore/RequestLoggingOptions.cs +++ b/src/Serilog.AspNetCore/AspNetCore/RequestLoggingOptions.cs @@ -35,13 +35,12 @@ static LogEventLevel DefaultGetLevel(HttpContext ctx, double _, Exception? ex) = : LogEventLevel.Information; 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)) - }; + ]; /// /// Gets or sets the message template. The default value is diff --git a/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj b/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj index 875f204..26da33d 100644 --- a/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj +++ b/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj @@ -35,10 +35,7 @@ - - - diff --git a/test/Serilog.AspNetCore.Tests/SerilogWebHostBuilderExtensionsTests.cs b/test/Serilog.AspNetCore.Tests/SerilogWebHostBuilderExtensionsTests.cs index 7d4f8a1..61ea07a 100644 --- a/test/Serilog.AspNetCore.Tests/SerilogWebHostBuilderExtensionsTests.cs +++ b/test/Serilog.AspNetCore.Tests/SerilogWebHostBuilderExtensionsTests.cs @@ -71,12 +71,11 @@ public async Task RequestLoggingMiddlewareShouldEnrichWithCustomisedProperties() { 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"); @@ -164,7 +163,7 @@ WebApplicationFactory Setup( return web; } - + [Fact] public async Task RequestLoggingMiddlewareShouldAddTraceAndSpanIds() { diff --git a/test/Serilog.AspNetCore.Tests/Support/SerilogSink.cs b/test/Serilog.AspNetCore.Tests/Support/SerilogSink.cs index 62e5966..a231d32 100644 --- a/test/Serilog.AspNetCore.Tests/Support/SerilogSink.cs +++ b/test/Serilog.AspNetCore.Tests/Support/SerilogSink.cs @@ -8,7 +8,7 @@ namespace Serilog.AspNetCore.Tests.Support; public class SerilogSink : ILogEventSink { - public List Writes { get; set; } = new(); + public List Writes { get; set; } = []; public void Emit(LogEvent logEvent) { From 92bc15aa27f925bb590ad7940f0272013fd2ead3 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Fri, 5 Apr 2024 13:28:38 +1000 Subject: [PATCH 4/6] README and sample updates to match recent .NET hosting models --- README.md | 63 ++++++++------------- samples/Sample/Program.cs | 98 ++++++++++++++++++++------------- samples/Sample/Startup.cs | 53 ------------------ samples/Sample/appsettings.json | 5 +- 4 files changed, 87 insertions(+), 132 deletions(-) delete mode 100644 samples/Sample/Startup.cs diff --git a/README.md b/README.md index 600ce17..4b5ae29 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,10 @@ Serilog logging for ASP.NET Core. This package routes ASP.NET Core log messages With _Serilog.AspNetCore_ installed and configured, you can write log messages directly through Serilog or any `ILogger` interface injected by ASP.NET. All loggers will use the same underlying implementation, levels, and destinations. -**.NET Framework** and .NET Core 2.x are supported by version 3.4.0 of this package. Recent versions of _Serilog.AspNetCore_ require .NET Core 3.x, .NET 5, or later. +**Versioning:** This package tracks the versioning and target framework support of its +[_Microsoft.Extensions.Hosting_](https://nuget.org/packages/Microsoft.Extensions.Hosting) dependency. Most users should choose the version of _Serilog.AspNetCore_ that matches +their application's target framework. I.e. if you're targeting .NET 7.x, choose a 7.x version of _Serilog.AspNetCore_. If +you're targeting .NET 8.x, choose an 8.x _Serilog.AspNetCore_ version, and so on. ### Instructions @@ -28,11 +31,9 @@ try Log.Information("Starting web application"); var builder = WebApplication.CreateBuilder(args); - - builder.Host.UseSerilog(); // <-- Add this line + builder.Services.AddSerilog(); // <-- Add this line var app = builder.Build(); - app.MapGet("/", () => "Hello World!"); app.Run(); @@ -47,23 +48,21 @@ finally } ``` -The `builder.Host.UseSerilog()` call will redirect all log events through your Serilog pipeline. +The `builder.Services.AddSerilog()` call will redirect all log events through your Serilog pipeline. **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: ``` -[22:14:44.646 DBG] RouteCollection.RouteAsync - Routes: - Microsoft.AspNet.Mvc.Routing.AttributeRoute - {controller=Home}/{action=Index}/{id?} - Handled? True -[22:14:44.647 DBG] RouterMiddleware.Invoke - Handled? True -[22:14:45.706 DBG] /lib/jquery/jquery.js not modified -[22:14:45.706 DBG] /css/site.css not modified -[22:14:45.741 DBG] Handled. Status code: 304 File: /css/site.css +[12:01:43 INF] Starting web application +[12:01:44 INF] Now listening on: http://localhost:5000 +[12:01:44 INF] Application started. Press Ctrl+C to shut down. +[12:01:44 INF] Hosting environment: Development +[12:01:44 INF] Content root path: serilog-aspnetcore/samples/Sample +[12:01:47 WRN] Failed to determine the https port for redirect. +[12:01:47 INF] Hello, world! +[12:01:47 INF] HTTP GET / responded 200 in 95.0581 ms ``` **Tip:** to see Serilog output in the Visual Studio output window when running under IIS, either select _ASP.NET Core Web Server_ from the _Show output from_ drop-down list, or replace `WriteTo.Console()` in the logger configuration with `WriteTo.Debug()`. @@ -97,12 +96,16 @@ Or [as JSON](https://github.com/serilog/serilog-formatting-compact): } ``` -To enable the middleware, first change the minimum level for `Microsoft.AspNetCore` to `Warning` in your logger configuration or _appsettings.json_ file: +To enable the middleware, first change the minimum level for the noisy ASP.NET Core log sources to `Warning` in your logger configuration or _appsettings.json_ file: ```csharp - .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning) + .MinimumLevel.Override("Microsoft.AspNetCore.Hosting", LogEventLevel.Warning) + .MinimumLevel.Override("Microsoft.AspNetCore.Mvc", LogEventLevel.Warning) + .MinimumLevel.Override("Microsoft.AspNetCore.Routing", LogEventLevel.Warning) ``` +> **Tip:** add `{SourceContext}` to your console logger's output template to see the names of loggers; this can help track down the source of a noisy log event to suppress. + Then, in your application's _Program.cs_, add the middleware with `UseSerilogRequestLogging()`: ```csharp @@ -187,11 +190,11 @@ Log.Logger = new LoggerConfiguration() .CreateBootstrapLogger(); // <-- Change this line! ``` -Then, pass a callback to `UseSerilog()` that creates the final logger: +Then, pass a callback to `AddSerilog()` that creates the final logger: ```csharp -builder.Host.UseSerilog((context, services, configuration) => configuration - .ReadFrom.Configuration(context.Configuration) +builder.Services.AddSerilog((services, lc) => lc + .ReadFrom.Configuration(builder.Configuration) .ReadFrom.Services(services) .Enrich.FromLogContext() .WriteTo.Console()); @@ -201,7 +204,7 @@ It's important to note that the final logger **completely replaces** the bootstr #### Consuming `appsettings.json` configuration -**Using two-stage initialization**, insert the `ReadFrom.Configuration(context.Configuration)` call shown in the example above. The JSON configuration syntax is documented in [the _Serilog.Settings.Configuration_ README](https://github.com/serilog/serilog-settings-configuration). +**Using two-stage initialization**, insert the `ReadFrom.Configuration(builder.Configuration)` call shown in the example above. The JSON configuration syntax is documented in [the _Serilog.Settings.Configuration_ README](https://github.com/serilog/serilog-settings-configuration). #### Injecting services into enrichers and sinks @@ -213,20 +216,6 @@ It's important to note that the final logger **completely replaces** the bootstr * `ILogEventSink` * `LoggingLevelSwitch` -#### Enabling `Microsoft.Extensions.Logging.ILoggerProvider`s - -Serilog sends events to outputs called _sinks_, that implement Serilog's `ILogEventSink` interface, and are added to the logging pipeline using `WriteTo`. _Microsoft.Extensions.Logging_ has a similar concept called _providers_, and these implement `ILoggerProvider`. Providers are what the default logging configuration creates under the hood through methods like `AddConsole()`. - -By default, Serilog ignores providers, since there are usually equivalent Serilog sinks available, and these work more efficiently with Serilog's pipeline. If provider support is needed, it can be optionally enabled. - -To have Serilog pass events to providers, **using two-stage initialization** as above, pass `writeToProviders: true` in the call to `UseSerilog()`: - -```csharp -builder.Host.UseSerilog( - (hostingContext, services, loggerConfiguration) => /* snip! */, - writeToProviders: true) -``` - ### JSON output The `Console()`, `Debug()`, and `File()` sinks all support JSON-formatted output natively, via the included _Serilog.Formatting.Compact_ package. @@ -286,7 +275,3 @@ using (LogContext.PushProperty("OperationType", "update")) // UserId and OperationType are set for all logging events in these brackets } ``` - -### Versioning - -This package tracks the versioning and target framework support of its (indirect) [_Microsoft.Extensions.Hosting_](https://nuget.org/packages/Microsoft.Extensions.Hosting) dependency. diff --git a/samples/Sample/Program.cs b/samples/Sample/Program.cs index 22155c4..9827b8f 100644 --- a/samples/Sample/Program.cs +++ b/samples/Sample/Program.cs @@ -1,47 +1,69 @@ using Serilog; +using Serilog.Events; using Serilog.Templates; +using Serilog.Templates.Themes; -namespace Sample; +// 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(); -public static class Program +Log.Information("Starting up!"); + +try { - public static int Main(string[] args) + var builder = WebApplication.CreateBuilder(args); + + builder.Services.AddSerilog((services, lc) => lc + .ReadFrom.Configuration(builder.Configuration) + .ReadFrom.Services(services) + .Enrich.FromLogContext() + .WriteTo.Console(new ExpressionTemplate( + // Include trace and span ids when present. + "[{@t:HH:mm:ss} {@l:u3}{#if @tr is not null} ({substring(@tr,0,4)}:{substring(@sp,0,4)}){#end}] {@m}\n{@x}", + theme: TemplateTheme.Code))); + + builder.Services.AddControllersWithViews(); + + await using var app = builder.Build(); + + // Configure the HTTP request pipeline. + if (!app.Environment.IsDevelopment()) { - // 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!"); - - try - { - CreateHostBuilder(args).Build().Run(); - - Log.Information("Stopped cleanly"); - return 0; - } - catch (Exception ex) - { - Log.Fatal(ex, "An unhandled exception occurred during bootstrapping"); - return 1; - } - finally - { - Log.CloseAndFlush(); - } + 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(); } + + // 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(); + + app.UseAuthorization(); + + app.MapControllerRoute( + name: "default", + pattern: "{controller=Home}/{action=Index}/{id?}"); - 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(new ExpressionTemplate( - // Include trace and span ids when present. - "[{@t:HH:mm:ss} {@l:u3}{#if @tr is not null} ({substring(@tr,0,4)}:{substring(@sp,0,4)}){#end}] {@m}\n{@x}"))) - .ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup()); + await app.RunAsync(); + + Log.Information("Stopped cleanly"); + return 0; +} +catch (Exception ex) +{ + Log.Fatal(ex, "An unhandled exception occurred during bootstrapping"); + return 1; +} +finally +{ + Log.CloseAndFlush(); } diff --git a/samples/Sample/Startup.cs b/samples/Sample/Startup.cs deleted file mode 100644 index 67200ce..0000000 --- a/samples/Sample/Startup.cs +++ /dev/null @@ -1,53 +0,0 @@ -using Serilog; - -namespace Sample; - -public class Startup -{ - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } - - 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 configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - 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.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.UseRouting(); - - app.UseAuthorization(); - - app.UseEndpoints(endpoints => - { - endpoints.MapControllerRoute( - name: "default", - pattern: "{controller=Home}/{action=Index}/{id?}"); - }); - } -} \ No newline at end of file diff --git a/samples/Sample/appsettings.json b/samples/Sample/appsettings.json index 29e11e4..b7aa0a7 100644 --- a/samples/Sample/appsettings.json +++ b/samples/Sample/appsettings.json @@ -3,8 +3,9 @@ "MinimumLevel": { "Default": "Information", "Override": { - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" + "Microsoft.AspNetCore.Mvc": "Warning", + "Microsoft.AspNetCore.Routing": "Warning", + "Microsoft.AspNetCore.Hosting": "Warning" } }, "WriteTo": [ From 70808dfda6e3618942041c9b578dce29d8033f83 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Sat, 6 Apr 2024 08:13:51 +1000 Subject: [PATCH 5/6] Fix comment in sample code [skip ci] --- samples/Sample/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/Sample/Program.cs b/samples/Sample/Program.cs index 9827b8f..7d334df 100644 --- a/samples/Sample/Program.cs +++ b/samples/Sample/Program.cs @@ -4,7 +4,7 @@ using Serilog.Templates.Themes; // 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 +// logger configured in `AddSerilog()` below, once configuration and dependency-injection have both been // set up successfully. Log.Logger = new LoggerConfiguration() .WriteTo.Console() From 77bd65c8cb0d9847ebac0fdcf5a295828c25bc7b Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Sat, 13 Jul 2024 07:44:23 +1000 Subject: [PATCH 6/6] Update Serilog.Settings.Configuration dependency to avoid transitive dependency on vulnerable System.Text.Json --- 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 26da33d..8ffcdb0 100644 --- a/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj +++ b/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj @@ -34,7 +34,7 @@ - +