Skip to content

Serilog

ObservabilityLoggingC#

What it is

Serilog is a structured logging library for .NET that treats log events as data with named properties rather than formatted strings.

Log messages use named holes rather than interpolation, so each value is captured as a queryable property alongside the rendered text.

Installation

dotnet add package Serilog.AspNetCore

Getting started

The smallest useful thing you can do with it, and what each part means.

Structured events
Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Information()
    .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
    .Enrich.FromLogContext()
    .WriteTo.Console()
    .WriteTo.File("logs/app-.log", rollingInterval: RollingInterval.Day)
    .CreateLogger();

// Named holes — NOT string interpolation.
log.Information("Order {OrderId} shipped to {Customer} for {Total:C}",
    order.Id, order.Customer, order.Total);

// $"Order {order.Id} shipped" would log one opaque string with no
// queryable properties — this is the single most common mistake.
The whole point is that OrderId and Customer become indexed fields. Interpolating destroys that and produces a log you cannot query.

Advanced usage

Where the library earns its place over a simpler alternative.

Context, destructuring and request logging
// Scope properties onto every event inside the block.
using (LogContext.PushProperty("CorrelationId", correlationId))
{
    log.Information("Processing started");
    // …everything here carries CorrelationId
}

// @ serialises the object rather than calling ToString().
log.Information("Received {@Order}", order);

// ASP.NET Core: one tidy line per request instead of six.
app.UseSerilogRequestLogging(options =>
{
    options.EnrichDiagnosticContext = (ctx, http) =>
        ctx.Set("UserId", http.User?.FindFirst("sub")?.Value);
});
The @ destructuring operator is easy to miss: without it, a complex object logs as its type name. With it, the whole shape becomes queryable.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

Logs are missing after the process exits
Buffered sinks were not flushed. Call Log.CloseAndFlush() in a finally block around the host run.
Properties are not queryable in the log platform
The message used interpolation. Switch to {Named} holes with arguments.

Best practices

  • Use message templates with named holes; never use string interpolation in a log call.
  • Override minimum levels per namespace so framework noise does not drown your own events.
  • Use @ to destructure objects and never log secrets, tokens or full request bodies.
  • Call Log.CloseAndFlush() on shutdown so buffered events are written.

Background

Why it exists, and what it was reacting to.

Created by Nicholas Blumhardt, Serilog introduced structured logging to .NET before the platform had it built in. Its sink ecosystem covers essentially every log destination in use.