# Structured Logging In ASP.NET Core With Serilog

> Serilog is a popular .NET logging library, and what makes it unique is structured logging out of the box. Apply the same message format to every log and they become easy to search and analyze. Let's install Serilog and configure it in an ASP.NET Core application.

Published: 2023-02-18. Author: Milan Jovanović.

Canonical: https://milanjovanovic.tech/blog/structured-logging-in-asp-net-core-with-serilog

Serilog gives ASP.NET Core structured logging out of the box.
Install the `Serilog.AspNetCore` package, call `UseSerilog` on the host builder, and configure the sinks in `appsettings.json`.
Then log with message templates like `{@Book}` so the values are stored as searchable properties, not flattened into text.

**Structured logging** is a practice where you apply the same message format to all of your
application logs.
The end result is that all your logs will have a similar structure, allowing them to be
easily searched and analyzed.

[Serilog](https://serilog.net/) is a popular logging library in .NET, packed with many features.
It provides logging to files, logging to the console, and elsewhere.

However, **Serilog** is unique because it comes with support for **structured logging** out of the box.

Let's see how we can install **Serilog** and configure it an **ASP.NET Core** application.

## Installing Serilog

To install **Serilog** in **ASP.NET Core** you can add the following NuGet package:

```powershell
Install-Package Serilog.AspNetCore
```

This NuGet packages comes with a simple API to integrate **Serilog** into your application.
You can call the `UseSerilog` method on the `HostBuilder` instance to provide a lambda
method to configure **Serilog**.

I think the most flexible way to configure Serilog is through application settings,
which is achieved by calling `ReadFrom.Configuration()`.

You can also call the `UseSerilogRequestLogging()` method to introduce automatic HTTP request logging
in your API.

```csharp
var builder = WebApplication.CreateBuilder(args);

builder.Host.UseSerilog((context, configuration) =>
    configuration.ReadFrom.Configuration(context.Configuration));

var app = builder.Build();

app.UseSerilogRequestLogging();

app.Run();
```

The next question is how do you provide the actual configuration values to **Serilog**?

## Configuring Serilog With `appsettings.json`

You need to add a `Serilog` section in your `appsettings.json` file.

Here you can configure, among other things:

- Which **sinks** to use with **Serilog**
- Override default and minimum log levels
- Configure file logging arguments

In this example, we're adding the `Console` and `File` sinks to **Serilog**.
And we're adding some additional configuration for the `File` sink in the `Serilog.WriteTo` configuration section.
We can configure the output path for the log files, the naming format, which formatter to use for the logs and so on.

```json
"Serilog": {
  "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
  "MinimumLevel": {
    "Default": "Information",
    "Override": {
      "Microsoft": "Warning",
      "System": "Warning"
    }
  },
  "WriteTo": [
    { "Name": "Console" },
    {
      "Name": "File",
      "Args": {
        "path": "/logs/log-.txt",
        "rollingInterval": "Day",
        "rollOnFileSizeLimit": true,
        "formatter": "Serilog.Formatting.Compact.CompactJsonFormatter, Serilog.Formatting.Compact"
      }
    }
  ],
  "Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ]
}
```

You can get a more detailed overview of what's supported with the `Serilog.Configuration` library in the
[documentation](https://github.com/serilog/serilog-settings-configuration).

## Using Serilog In ASP.NET Core

We managed to successfully install and configure **Serilog**.
But how do we actually use it?

**Serilog** integrates with the `ILogger` interaface coming from the `Microsoft.Extensions.Logging` namespace.
If you're already using `ILogger` for logging, everything will continue working correctly.

Here's a simple example of logging inside of a [**Minimal API**](https://milanjovanovic.tech/blog/minimal-apis-dotnet) endpoint:

```csharp
app.MapGet("/serilog-is-cool", (ILogger logger) =>
{
    logger.LogInformation("This is a log inside of the Minimal API endpoint.");

    return Results.Ok(new { Message = "success" });
});
```

You just inject an `ILogger` or `ILogger<T>` instance and Serilog will provide its own implementation at runtime.

## Structured Logging Syntax

The idea behind **structured logging** is that you can introduce additional contextual information inside
of your logs.
**Serilog** does this using a message template syntax, where you can specify named parameters and then
pass in their values separately.

Here's an example of what this message template would look like.
You specify parameters inside of curly bracers and provide a name, for example `{NamedParameter}`.
The value provided for the parameter will be serialized as a property inside of the corresponding
structured log.

```csharp
var book = new { Author = "Domain-Driven Design", Title = "Eric Evans" };
var orderNumber = 1;

log.LogInformation(
    "Processing book {@Book}, order number = {@OrderNumber}",
    book,
    orderNumber);
```

There are a few things to unpack here:

- `{@Book}` parameter which accepts an object
- `{OrderNumber}` parameter which accepts a scalar value

The `@` operator in front of `Book` tells Serilog to serialize the object passed in, instead of converting
it using `ToString()`.

## Benefits Of Structured Logging

Lastly, I want to highlight what are some of the benefits of **structured logging** and why you should be using it.

As I said at the beginning, the main idea with **structured logging** is that all log message follow the same
structure.
This structure can be a JSON document for example, or a row in a relational table.
Since structured logs are in a machine-readable format, it's much easier to search through them for
specific information.

When an error occurs, structured logs can provide more context and details about the error, making it easier to
identify the root cause and fix the problem.

It's very easy to start doing structured logging with **Serilog**, and I hope you'll give it a try.

See you next week, and have an excellent Saturday.

---

## Frequently asked questions

### What is structured logging?

Structured logging is a practice where you apply the same message format to all of your application logs, for example a JSON document. Because the logs are machine readable and share a structure, they are much easier to search and analyze.

### How do you add Serilog to an ASP.NET Core application?

Install the Serilog.AspNetCore NuGet package and call UseSerilog on the host builder, reading the settings with ReadFrom.Configuration. You can also call UseSerilogRequestLogging to get automatic HTTP request logging in your API.

### How do you configure Serilog in appsettings.json?

Add a Serilog section that lists the sinks to use (for example Console and File), overrides default and minimum log levels, and configures sink arguments such as the log file path, rolling interval, and formatter.

### Does Serilog work with the built-in ILogger interface?

Yes. Serilog integrates with the ILogger interface from Microsoft.Extensions.Logging. You inject ILogger or ILogger<T> as usual and Serilog provides its own implementation at runtime, so existing logging code keeps working.

### What does the @ operator do in a Serilog message template?

In a message template parameter like {@Book}, the @ operator tells Serilog to serialize the object passed in as a structured property, instead of converting it to text with ToString().
