CodeToClarity Logo
Published on ·6 min read·.NET

Structured Logging with Serilog in .NET: A Practical Guide

Kishan KumarKishan Kumar

Learn how to implement structured logging in .NET using Serilog. Step-by-step guide with practical examples, sinks configuration, and Seq integration for better log management.

If you've ever spent hours scrolling through plain-text log files trying to find that one error message, you know the pain. Traditional logging can feel like searching for a needle in a haystack → especially when your application grows and logs start piling up.

That's where structured logging comes in, and trust me, once you start using it, there's no going back.

In this guide, we'll explore how to implement structured logging in .NET using Serilog, one of the most popular logging libraries in the .NET ecosystem. We'll keep things practical → no overwhelming theory, just real code you can use right away.

What is Structured Logging?

Let's start with a quick comparison.

Traditional logging looks like this:

logger.LogInformation("User Kishan Kumar logged in at 2026-01-15 10:30:00");

It's just a string. If you want to search for all logs related to "Kishan Kumar," you'd have to parse text files manually or use regex. Not fun.

Structured logging looks like this:

logger.LogInformation("User {UserName} logged in at {LoginTime}", "Kishan Kumar", DateTime.Now);

Now your log isn't just text → it's a structured event with key-value pairs. Behind the scenes, it might look like this in JSON:

{
  "Timestamp": "2026-01-15T10:30:00Z",
  "Level": "Information",
  "Message": "User Kishan Kumar logged in at 2026-01-15 10:30:00",
  "UserName": "Kishan Kumar",
  "LoginTime": "2026-01-15T10:30:00Z"
}

See the difference? You can now query, filter, and analyze your logs based on UserName or LoginTime. It's like upgrading from a text file to a searchable database.

Why Serilog?

There are several logging libraries for .NET, but Serilog stands out because:

  • Easy to configure – minimal setup, maximum flexibility
  • Structured by default – designed for modern logging needs
  • Rich ecosystem of sinks – write logs to console, files, databases, cloud services, and more
  • Widely adopted – battle-tested in production by thousands of developers

What is a Sink?

Think of a sink as a destination for your logs.

When your application logs something, where should that log go? To the console? A file? A database? Maybe a specialized logging platform like Seq or Elasticsearch?

Serilog lets you configure multiple sinks simultaneously. For example, you might:

  • Log to the console for local development
  • Log to a file for historical records
  • Log to Seq for advanced querying and visualization

Let's see how this works in practice.

Setting Up Serilog in .NET 8

I'll demonstrate using a simple .NET Web API project, but the same approach works for any .NET application.

Step 1: Install NuGet Packages

First, install the necessary packages:

dotnet add package Serilog
dotnet add package Serilog.Extensions.Logging
dotnet add package Serilog.Sinks.Console
dotnet add package Serilog.Sinks.File

Here's what each package does:

  • Serilog – the core library
  • Serilog.Extensions.Logging – integrates Serilog with Microsoft's ILogger interface
  • Serilog.Sinks.Console – writes logs to the console
  • Serilog.Sinks.File – writes logs to files

Step 2: Configure Serilog in Program.cs

Open your Program.cs and configure Serilog:

using Serilog;

var builder = WebApplication.CreateBuilder(args);

Log.Logger = new LoggerConfiguration()
    .WriteTo.Console()
    .WriteTo.File("logs/codetoclarity-app.txt", rollingInterval: RollingInterval.Day)
    .CreateLogger();

builder.Logging.AddSerilog();

var app = builder.Build();

app.MapControllers();

app.Run();

Log.CloseAndFlush();

What's happening here?

  • Log.Logger = new LoggerConfiguration()... – creates a global Serilog logger
  • .WriteTo.Console() – logs to the console (great for development)
  • .WriteTo.File(...) – logs to a file, creating a new file every day
  • builder.Logging.AddSerilog() – integrates Serilog with .NET's logging system
  • Log.CloseAndFlush() – ensures all logs are written before the app shuts down

That's it! Serilog is now configured and ready to use.

Exploring Different Sinks

Let's look at the most common sinks and when to use them.

Console Sink

Perfect for local development. You'll see logs in real-time as your app runs.

.WriteTo.Console()

Pros:

  • Instant feedback
  • Easy to debug

Cons:

  • Logs disappear when the app stops
  • Not suitable for production

File Sink

Writes logs to a file. Great for keeping a historical record of what happened in your application.

.WriteTo.File("logs/codetoclarity-app.txt", rollingInterval: RollingInterval.Day)

The rollingInterval: RollingInterval.Day option creates a new file every day, so your logs don't grow infinitely. You'll end up with files like:

  • codetoclarity-app-20260115.txt
  • codetoclarity-app-20260116.txt

Pros:

  • Simple and reliable
  • Logs persist across restarts

Cons:

  • Files can grow large
  • Searching through files manually is painful

Database Sink

Logs directly to a database like SQL Server, PostgreSQL, or MySQL.

dotnet add package Serilog.Sinks.MSSqlServer
.WriteTo.MSSqlServer(
    connectionString: "Data Source=localhost;Initial Catalog=CodeToClarityLogs;Integrated Security=SSPI",
    sinkOptions: new MSSqlServerSinkOptions
    {
        TableName = "Logs",
        SchemaName = "dbo",
        AutoCreateSqlTable = true
    })

Pros:

  • Centralized storage
  • Query logs using SQL

Cons:

  • Can impact database performance
  • Requires careful management of storage

Seq Sink

This is where things get really powerful.

Seq is a specialized logging platform designed for structured logs. It provides a web-based UI where you can search, filter, and visualize your logs with ease.

Install the package:

dotnet add package Serilog.Sinks.Seq

Configure the sink:

.WriteTo.Seq("http://localhost:5341")

After installing Seq locally, it runs on port 5341 by default. Open your browser and navigate to http://localhost:5341 to access the Seq dashboard.

We'll explore Seq in more detail shortly.

Structured Logging in Action

Here's where Serilog really shines. Instead of logging plain strings, you log events with properties.

string firstName = "Kishan";
string lastName = "Kumar";

_logger.LogInformation("User logged in: {FirstName} {LastName}", firstName, lastName);

This creates a structured log event that looks like this in JSON:

{
  "Timestamp": "2026-01-15T10:30:00Z",
  "Level": "Information",
  "MessageTemplate": "User logged in: {FirstName} {LastName}",
  "Properties": {
    "FirstName": "Kishan",
    "LastName": "Kumar"
  }
}

Now you can search for all logs where FirstName = "Kishan" or LastName = "Kumar". Try doing that with plain text logs!

Why This Matters

  1. Better searchability – filter logs by specific properties
  2. Improved analytics – count errors by user, track processing times, etc.
  3. Richer context – understand exactly what was happening when an event occurred

Real-World Example

Let's say you have a simple weather API:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    private readonly ILogger<WeatherForecastController> _logger;

    public WeatherForecastController(ILogger<WeatherForecastController> logger)
    {
        _logger = logger;
    }

    [HttpGet(Name = "GetWeatherForecast")]
    public IEnumerable<WeatherForecast> Get()
    {
        _logger.LogInformation(
            "Fetching weather data from {Controller}/{Action}", 
            nameof(WeatherForecastController), 
            nameof(Get));

        try
        {
            return GenerateRandomWeatherValues();
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to generate weather forecast");
            throw;
        }
    }

    private IEnumerable<WeatherForecast> GenerateRandomWeatherValues()
    {
        // Your logic here
        return new List<WeatherForecast>();
    }
}

Viewing Logs in Seq

Run your application and make a request to the weather endpoint. Then open Seq at http://localhost:5341.

You'll see all your logs displayed in a beautiful interface:

  • Information logs appear with an info icon
  • Error logs appear with a red error icon

Click on any log to expand it and see:

  • The full exception message and stack trace
  • Structured properties like Controller and Action
  • Request details like RequestPath
  • Source context

This makes debugging so much easier. No more grepping through text files!

Best Practices

  1. Use meaningful property names{UserId} is better than {Id}
  2. Don't log sensitive data – avoid passwords, credit card numbers, etc.
  3. Use appropriate log levelsInformation, Warning, Error, Critical
  4. Set up log retention policies – don't let logs consume all your disk space
  5. Monitor your logs – set up alerts for critical errors

Wrapping Up

Structured logging with Serilog transforms how you handle logs in .NET applications. What used to be painful text-file archaeology becomes elegant, queryable data.

Start small:

  • Add Serilog to one project
  • Configure a few sinks
  • Use structured properties in your logs
  • Explore Seq or another log viewer

You'll wonder how you ever lived without it.