.NET Core  

Modern Logging in .NET with Serilog

Introduction

This article focus is on way to implement structured logging in ASP.NET Core using Serilog. Setting up robust logging from the start is essential for effective monitoring and debugging. This guide explores how to implement Serilog to create a high-performance, structured logging system for modern applications.

Serilog is premier third-party logging library that seamlessly integrates with the native .NET ILogger interface. It allows applications to direct logs to diverse destinations—including consoles, files, databases etc. By providing structured logging, Serilog ensures data is stored in a searchable format for cleaner analysis. In terms of performance, Serilog has a negligible impact on application speed. By utilizing features such as asynchronous logging and message batching, it ensures that logging operations do not hinder the responsiveness of the application.

Setup

  1. To begin, install the Serilog integration package for ASP.NET Core using the following command:

Install-Package Serilog.AspNetCore
  1. After installation, configure the logger within Program.cs. The following setup initializes the logger at the application entry point and integrates it into the host:

using Serilog;

// Setup a temporary logger for the startup process
Log.Logger = new LoggerConfiguration()
    .WriteTo.Console()
    .CreateBootstrapLogger();

try
{
    Log.Information("Initializing the Web Host...");

    var webAppBuilder = WebApplication.CreateBuilder(args);

    // Replace default logging with Serilog
    webAppBuilder.Host.UseSerilog((hostingContext, loggerConfig) =>
    {
        loggerConfig
            .ReadFrom.Configuration(hostingContext.Configuration)
            .Enrich.FromLogContext()
            .WriteTo.Console();
    });

    webAppBuilder.Services.AddEndpointsApiExplorer();
    webAppBuilder.Services.AddSwaggerGen();

    var appInstance = webAppBuilder.Build();

    if (appInstance.Environment.IsDevelopment())
    {
        appInstance.UseSwagger();
        appInstance.UseSwaggerUI();
    }

    appInstance.UseHttpsRedirection();
    appInstance.Run();
}
catch (Exception startupException)
{
    Log.Fatal(startupException, "The application host terminated unexpectedly during startup.");
}
finally
{
    // Ensure all logs are written before the app closes
    Log.CloseAndFlush();
}

Initially, we are creating the logger instance using Serilog while enabling it to write logs to the console. Note that the only purpose of this piece of code is to enable logging within the Program.cs.
As we navigate, we are integrating Serilog into the ASP.NET Core Dependency Injection (DI) container that ensures its implementation is used by every ILogger<> instance throughout the application. In this setup, two primary configurations are established: directing output to the Console and enabling the logger to ReadFrom.Configuration. Consequently, the framework bypasses the standard "Logging" section in appsettings.json, relying exclusively on the "Serilog" section for all logging behavior.

  1. The logging configuration is consolidated under the Serilog key to define clear boundaries for log filtering and verbosity

{
  "Serilog": {
    "MinimumLevel": {
      "Default": "Information",
      "Override": {
        "Microsoft": "Warning",
        "Microsoft.AspNetCore.Hosting.Diagnostics": "Error",
        "System": "Warning"
      }
    },
    "WriteTo": [
      { "Name": "Console" }
    ]
  },
  "AllowedHosts": "*"
}

The configuration sets the MinimumLevel to Information, ensuring only events of that priority or higher are recorded. Serilog utilizes six distinct log levels to categorize system events:

LevelUsage
VerboseExhaustive detail; rarely used in production.
DebugInternal events for troubleshooting and flow analysis.
InformationGeneral operational events and system functions.
WarningPotential issues or degraded system performance.
ErrorFunctional failures or broken application logic.
FatalCritical crashes requiring immediate intervention.

Serilog Sinks

Serilog uses Sinks to direct log data to various destinations. It supports a vast range of targets, including the Console, local files, SQL Server, MongoDB, and cloud services like Amazon CloudWatch. This flexibility allows for centralizing logs across different environments. The following sections demonstrate how to configure several popular Sinks directly through the appsettings.json file.

1. SQL

To log to SQL Server, install the required sink package via the .NET CLI


dotnet add package Serilog.Sinks.MSSqlServer

This is the preferred method, as it allows you to change database targets without recompiling the code. Add the Serilog.Sinks.MSSqlServer configuration to your appsettings.json file:

{
  "Serilog": {
    "WriteTo": [
      {
        "Name": "MSSqlServer",
        "Args": {
          "connectionString": "Server=localhost;Database=LogDb;User Id=sa;Password=password;",
          "sinkOptionsSection": {
            "tableName": "ApplicationLogs",
            "autoCreateSqlTable": true
          }
        }
      }
    ]
  }
}

Ensure your logger reads from the configuration so that the SQL Server sink is activated in Program.cs:

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

The sink will automatically create the ApplicationLogs table if it does not exist, providing a structured way to query errors using standard SQL.

2. Local file

To log to a local file, install the Serilog.Sinks.File package:

dotnet add package Serilog.Sinks.File

Update the WriteTo section to define the file path and rotation rules in appsettings.json. This ensures logs are archived daily and don't consume excessive disk space.

{
  "Serilog": {
    "WriteTo": [
      {
        "Name": "File",
        "Args": {
          "path": "Logs/log-.txt",
          "rollingInterval": "Day",
          "retainedFileCountLimit": 7,
          "formatter": "Serilog.Formatting.Json.JsonFormatter, Serilog"
        }
      }
    ]
  }
}

Ensure the application is configured to read these settings in Program.cs

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

Key Features

  • RollingInterval: Automatically creates a new file (e.g., daily) to prevent massive file sizes.

  • RetainedFileCountLimit: Deletes older logs automatically after a set period (e.g., 7 days).

  • Structured Format: Using JsonFormatter makes the files easy to parse for external log analysis tools.

Conclusion

In this article, we have seen how to implement structured logging in ASP.NET Core using Serilog for better application monitoring. We explored the benefits of different log levels and how to direct data to multiple destinations using various sinks. Hope you enjoyed reading it.