-
Notifications
You must be signed in to change notification settings - Fork 0
Serilog Extensions
MSL.Plumber.Serilog.Extensions adds one middleware that emits a single structured Serilog event when a request completes — carrying the request id, elapsed time, trace and span ids, and anything you enrich onto Serilog's DiagnosticContext during the request. It's the request-logging line you'd otherwise hand-write at the top of every pipeline.
This page covers wiring it up and the logging behavior. The full options surface lives on RequestLoggerOptions.
dotnet add package MSL.Plumber.Serilog.ExtensionsThe package targets .NET 10 and references the core MSL.Plumber.Pipeline package.
The package is two calls that split across the builder and the handler: AddSerilogRequestLogging registers what the middleware needs, and UseSerilogRequestLogging adds the middleware to the pipeline.
using Plumber;
using Plumber.Serilog;
using Serilog;
using Serilog.Formatting.Compact;
using var handler = RequestHandlerBuilder.Create<MyRequest, MyResponse>()
.ConfigureServices((services, _) =>
services.AddSerilogRequestLogging<MyRequest, MyResponse>(
logger => logger.WriteTo.Console(new CompactJsonFormatter())))
.Build()
.UseSerilogRequestLogging()
.Use<ValidationMiddleware>()
.Use<ProcessingMiddleware>();
var response = await handler.InvokeAsync(request);AddSerilogRequestLogging<TRequest, TResponse> registers Serilog's ILogger and DiagnosticContext and the RequestLoggerOptions infrastructure in one call — which is what makes the parameterless UseSerilogRequestLogging() self-sufficient. You build the logger inline through the Action<LoggerConfiguration>, so there's no static Log.Logger to set up. In the CreateBuilder/Configure pattern, AddSerilogRequestLogging belongs in CreateBuilder and UseSerilogRequestLogging in Configure.
The middleware needs three things in the provider — ILogger, DiagnosticContext, and IOptions<RequestLoggerOptions<TRequest, TResponse>>. AddSerilogRequestLogging is the one-call convenience that registers all three; if you already configure Serilog through a host or your own AddSerilog, register the options with AddOptions/Configure and the middleware resolves the rest.
The request type must be a reference type — the extension methods constrain TRequest : class.
Register UseSerilogRequestLogging first, as the outermost middleware. It wraps the rest of the pipeline in a try/catch and logs once on the way out, so registering it first lets it observe the whole pipeline's outcome — including exceptions thrown by anything downstream. Elapsed time comes from context.Elapsed, measured from the start of the request (see Request lifecycle), so the timing is whole-request regardless of placement.
One event per request, when the pipeline returns or throws:
- A success completes at the configured
LogLevel(defaultInformation). - A failure completes at
Error, with the exception attached, then rethrows unless you setThrowOnExceptiontofalse.
Every event carries the message-template properties (by default RequestId from context.Id and Elapsed from context.Elapsed.TotalMilliseconds), plus the current Activity trace id and span id when one is in flight — so request logs line up with distributed traces with no extra work.
Anything written to the DiagnosticContext during the request attaches to that single completion event. Use EnrichDiagnosticContext to add request- and response-derived properties:
handler.UseSerilogRequestLogging(options =>
{
options.EnrichDiagnosticContext = (diagnosticContext, context) =>
{
diagnosticContext.Set("Request", context.Request);
diagnosticContext.Set("Response", context.Response);
};
});One completion event then carries Request, Response, RequestId, Elapsed, and the trace ids — a single line that describes the whole request rather than a scatter of log statements.
Three ways to supply RequestLoggerOptions:
- At registration — pass
configureOptionstoAddSerilogRequestLogging, alongside the logger configuration. - Inline on the middleware — pass an
Action<RequestLoggerOptions<TRequest, TResponse>>toUseSerilogRequestLogging. Inline options take precedence over anything in the container. - From the container — register
Configure<RequestLoggerOptions<MyRequest, MyResponse>>(...)inConfigureServicesand call the parameterlessUseSerilogRequestLogging(). The middleware resolves them asIOptions<T>.
With nothing configured, the middleware runs on the defaults baked into RequestLoggerOptions.
- RequestLoggerOptions — the full options surface: level, message template, enrichment, exception handling
-
Request lifecycle —
RequestContext,Elapsed,Id, and the cancellation token the middleware honors -
Middleware —
Use<T>registration and pipeline ordering -
Building a pipeline —
ConfigureServicesand the builder surface
Documents Plumber v4.x · Repository · MIT License · Report an issue
Getting Started
Pipeline (core)
Testing
Serilog Extensions
Diagnostics
Recipes
- AWS Lambda — API Gateway
- AWS Lambda — SQS
- Azure Functions — HTTP
- SQS polling console
- ASP.NET Core integration
- BackgroundService worker
- Webhook receiver
- Multi-command CLI
- File watcher
- Configuration reload
Repo · NuGet · NuGet — Testing · NuGet — Serilog · NuGet — Diagnostics