Skip to content

Commit ebe9ea9

Browse files
committed
Log exception set in IDiagnosticContext
If there is no unhandled exception, then log the exception set in IDiagnosticContext.
1 parent 76f4a51 commit ebe9ea9

File tree

2 files changed

+57
-7
lines changed

2 files changed

+57
-7
lines changed

src/Serilog.AspNetCore/AspNetCore/RequestLoggingMiddleware.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ bool LogCompletion(HttpContext httpContext, DiagnosticContextCollector collector
8484
// Enrich diagnostic context
8585
_enrichDiagnosticContext?.Invoke(_diagnosticContext, httpContext);
8686

87-
if (!collector.TryComplete(out var collectedProperties))
87+
if (!collector.TryComplete(out var collectedProperties, out var collectedException))
8888
collectedProperties = NoProperties;
8989

9090
// Last-in (correctly) wins...
@@ -96,7 +96,7 @@ bool LogCompletion(HttpContext httpContext, DiagnosticContextCollector collector
9696
new LogEventProperty("Elapsed", new ScalarValue(elapsedMs))
9797
});
9898

99-
var evt = new LogEvent(DateTimeOffset.Now, level, ex, _messageTemplate, properties);
99+
var evt = new LogEvent(DateTimeOffset.Now, level, ex ?? collectedException, _messageTemplate, properties);
100100
logger.Write(evt);
101101

102102
return false;

test/Serilog.AspNetCore.Tests/SerilogWebHostBuilderExtensionsTests.cs

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using Microsoft.AspNetCore.Hosting;
1010
using Microsoft.AspNetCore.Mvc.Testing;
1111
using Microsoft.AspNetCore.Builder;
12+
using Microsoft.AspNetCore.Http;
1213
using Serilog.Filters;
1314
using Serilog.AspNetCore.Tests.Support;
1415

@@ -62,7 +63,51 @@ public async Task RequestLoggingMiddlewareShouldEnrich()
6263
Assert.True(completionEvent.Properties.ContainsKey("Elapsed"));
6364
}
6465

65-
WebApplicationFactory<TestStartup> Setup(ILogger logger, bool dispose, Action<RequestLoggingOptions> configureOptions = null)
66+
[Fact]
67+
public async Task RequestLoggingMiddlewareShouldEnrichWithCollectedExceptionIfNoUnhandledException()
68+
{
69+
var diagnosticContextException = new Exception("Exception set in diagnostic context");
70+
var (sink, web) = Setup(options =>
71+
{
72+
options.EnrichDiagnosticContext += (diagnosticContext, _) =>
73+
{
74+
diagnosticContext.SetException(diagnosticContextException);
75+
};
76+
});
77+
78+
await web.CreateClient().GetAsync("/resource");
79+
80+
var completionEvent = sink.Writes.First(logEvent => Matching.FromSource<RequestLoggingMiddleware>()(logEvent));
81+
82+
Assert.Same(diagnosticContextException, completionEvent.Exception);
83+
}
84+
85+
[Theory]
86+
[InlineData(false)]
87+
[InlineData(true)]
88+
public async Task RequestLoggingMiddlewareShouldEnrichWithUnhandledExceptionEvenIfExceptionIsSetInDiagnosticContext(bool setExceptionInDiagnosticContext)
89+
{
90+
var diagnosticContextException = new Exception("Exception set in diagnostic context");
91+
var unhandledException = new Exception("Unhandled exception thrown in API action");
92+
var (sink, web) = Setup(options =>
93+
{
94+
options.EnrichDiagnosticContext += (diagnosticContext, _) =>
95+
{
96+
if (setExceptionInDiagnosticContext)
97+
diagnosticContext.SetException(diagnosticContextException);
98+
};
99+
}, actionCallback: _ => throw unhandledException);
100+
101+
Func<Task> act = () => web.CreateClient().GetAsync("/resource");
102+
103+
Exception thrownException = await Assert.ThrowsAsync<Exception>(act);
104+
var completionEvent = sink.Writes.First(logEvent => Matching.FromSource<RequestLoggingMiddleware>()(logEvent));
105+
Assert.Same(unhandledException, completionEvent.Exception);
106+
Assert.Same(unhandledException, thrownException);
107+
}
108+
109+
WebApplicationFactory<TestStartup> Setup(ILogger logger, bool dispose, Action<RequestLoggingOptions> configureOptions = null,
110+
Action<HttpContext> actionCallback = null)
66111
{
67112
var web = _web.WithWebHostBuilder(
68113
builder => builder
@@ -77,24 +122,29 @@ WebApplicationFactory<TestStartup> Setup(ILogger logger, bool dispose, Action<Re
77122
.Configure(app =>
78123
{
79124
app.UseSerilogRequestLogging(configureOptions);
80-
app.Run(_ => Task.CompletedTask); // 200 OK
125+
app.Run(ctx =>
126+
{
127+
actionCallback?.Invoke(ctx);
128+
return Task.CompletedTask;
129+
}); // 200 OK
81130
})
82131
.UseSerilog(logger, dispose));
83132

84133
return web;
85134
}
86135

87-
(SerilogSink, WebApplicationFactory<TestStartup>) Setup(Action<RequestLoggingOptions> configureOptions = null)
136+
(SerilogSink, WebApplicationFactory<TestStartup>) Setup(Action<RequestLoggingOptions> configureOptions = null,
137+
Action<HttpContext> actionCallback = null)
88138
{
89139
var sink = new SerilogSink();
90140
var logger = new LoggerConfiguration()
91141
.Enrich.FromLogContext()
92142
.WriteTo.Sink(sink)
93143
.CreateLogger();
94144

95-
var web = Setup(logger, true, configureOptions);
145+
var web = Setup(logger, true, configureOptions, actionCallback);
96146

97147
return (sink, web);
98148
}
99149
}
100-
}
150+
}

0 commit comments

Comments
 (0)