Skip to content

Serilog Extensions

Mark Lauter edited this page Jun 13, 2026 · 3 revisions

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.

Install

dotnet add package MSL.Plumber.Serilog.Extensions

The package targets .NET 10 and references the core MSL.Plumber.Pipeline package.

Wiring it up

The middleware resolves Serilog's ILogger and DiagnosticContext from the pipeline's service provider, so register Serilog in ConfigureServices with AddSerilog before you register the middleware:

using Plumber;
using Plumber.Serilog.Extensions;
using Microsoft.Extensions.DependencyInjection;
using Serilog;

Log.Logger = new LoggerConfiguration()
    .Enrich.FromLogContext()
    .WriteTo.Console()
    .CreateLogger();

using var handler = RequestHandlerBuilder.Create<MyRequest, MyResponse>()
    .ConfigureServices((services, _) => services.AddSerilog(Log.Logger))
    .Build()
    .UseSerilogRequestLogging()
    .Use<ValidationMiddleware>()
    .Use<ProcessingMiddleware>();

var response = await handler.InvokeAsync(request);

AddSerilog (from Serilog.Extensions.Hosting) registers both the ILogger and the DiagnosticContext the middleware needs. Skip it and the middleware throws on first invocation when those dependencies fail to resolve.

The request type must be a reference type — the extension methods constrain TRequest : class.

Where it sits in the pipeline

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.

What it logs

One event per request, when the pipeline returns or throws:

  • A success completes at the configured LogLevel (default Information).
  • A failure completes at Error, with the exception attached, then rethrows unless you set ThrowOnException to false.

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.

Enriching the event

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.

Configuring

Two ways to supply RequestLoggerOptions:

  • Inline, per registration — pass an Action<RequestLoggerOptions<TRequest, TResponse>> to UseSerilogRequestLogging, as above. Inline options take precedence.
  • From the container — register Configure<RequestLoggerOptions<MyRequest, MyResponse>>(...) in ConfigureServices and call the parameterless UseSerilogRequestLogging(). The middleware resolves them as IOptions<T>.

Call the parameterless overload with nothing registered and the middleware runs on the defaults baked into RequestLoggerOptions.

See also

  • RequestLoggerOptions — the full options surface: level, message template, enrichment, exception handling
  • Request lifecycleRequestContext, Elapsed, Id, and the cancellation token the middleware honors
  • MiddlewareUse<T> registration and pipeline ordering
  • Building a pipelineConfigureServices and the builder surface

Clone this wiki locally