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(); ``` 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/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj b/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj index c6ca09e..0dfd0d5 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 + 6.0.0 Microsoft;Serilog Contributors netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0 true @@ -30,7 +30,7 @@ - + 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