Configuring Serilog through appsettings.json file

https://mohsen.es/configuring-serilog-through-appsettings-json-file-33b26594bb46

!assets/1WdXcrIjj_nocCnsnTfI9CQ.jpeg Private or broken link
The page you're looking for is either not available or private!

In this post, I’m going to show you how to configure Serilog via the configuration file. Changing the logging configuration without touching the codebase is really helpful, especially in the production environment. First why Serilog? It is easy to set up, has a clean API, and is portable between recent .NET platforms. The big difference between Serilog and the other frameworks is that it is designed to do structured logging out of the box. Another thing I really like about Serilog is that it can be configured via the appsetting.json file alongside configuring through code.

Let’s start with creating a new project.

Create a new ASP.NET 7.0 Web API project.

Install Serilog.AspNetCore nuget package.

Open Program.cs file and add the following code:

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

UseSerilog sets Serilog as the logging provider. We are going to config Serilog via the appsettings.json file.

Open appsettings.json and appsettings.Development.json files and get rid of the logging section:

"Logging": {
  "LogLevel": {
    "Default": "Information",
    "Microsoft": "Warning",
    "Microsoft.Hosting.Lifetime": "Information"
  }
}

Add Serilog configuration section to appsettings.Development.json file:

"Serilog": {
  "MinimumLevel": {
    "Default": "Debug",
    "Override": {
      "Microsoft": "Information",
      "System": "Information"
    },
    "Using": [ ],
  },
  "WriteTo": [
    { }
  ]
}

Serilog.AspNetCore nuget package has a dependency on Serilog.Settings.Configuration nuget package and is a Serilog settings provider that reads from Microsoft.Extensions.Configuration sources. The above configuration is equivalent to this:

Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Debug()
    .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
    .MinimumLevel.Override("System", LogEventLevel.Information)
    .Enrich.FromLogContext()
    .CreateLogger();

Serilog uses sinks to write log events to storage for example database, file, etc. One of the most popular sinks for the debugging environment is the Console sink.

  • Install Serilog.Sinks.Consolenuget package
  • Add the following configuration:
"Serilog": {
  "MinimumLevel": {
    "Default": "Debug",
    "Override": {
      "Microsoft": "Information",
      "System": "Information"
    }
  },
  "Using": [ "Serilog.Sinks.Console" ],
  "WriteTo": [
    { "Name": "Console" }
  ]
}

In the Using section add the sink’s nuget package name "Using": [ "Serilog.Sinks.Console" ]

  • In the WriteTo section add sink name and arguments "WriteTo":[ { "Name": "Console" } ]

Run the project and in the console window you should see logs like below

Now we want to use SQL Server sink for other environments:

"Serilog": {
  "MinimumLevel": {
    "Default": "Information",
    "Override": {
      "Microsoft": "Error",
      "System": "Error"
    },
    "Using": [ "Serilog.Sinks.MSSqlServer" ]
  },
  "WriteTo": [
    {
      "Name": "MSSqlServer",
      "Args": {
        "connectionString": "ConnectionString",
        "tableName": "Logs",
        "autoCreateSqlTable": true
      }
    }
  ]
}

Well, configuring a sink via appsettings.json could be harder than configuring through the code, and for each sink, you might not be able to find a JSON configuration sample. Normally each sink accepts several parameters to configure the sink. For instance, the Console sink accepts the below parameters:

Each one of these parameters can be configured through JSON:

"WriteTo": [
  {
    "Name": "Console",
    "Args": {
      "restrictedToMinimumLevel": "Verbose",
      "outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} <s:{SourceContext}>{NewLine}{Exception}",
      "theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console"
    }
  }
]

After setting the console sink parameters, logs should be like the following image:

To see the complete SQL Server sink JSON configuration check out this appsettings.json file.

Log events can be enriched with properties in various ways. You can add additional data by enrichers. For instance, in the production environment, we want to add the IP of the client to the log events.

  • Install Serilog.Enrichers.ClientInfo package
  • Add enriched package name to Using section
  • Add Enrich section with WithClientIp value (enriched name normally starts with With word)
"Using": [ "Serilog.Sinks.MSSqlServer", "Serilog.Enrichers.ClientInfo" ],
"Enrich": [ "WithClientIp" ]

All events written through the logger will carry a property ClientIp with the IP of the client. Check out the list of available enrichers here.

By using filters you can exclude or include some log events.

  • Install Serilog.Expressions nuget package
  • Add the “Filter” section to Serilog settings
"Filter": [
  {
    "Name": "ByExcluding",
    "Args": {
      "expression": "RequestPath like '%swagger%'"
    }
  }
]

All log events that contain swagger will be excluded.

Moreover, you can log the HTTP requests. UseSerilogRequestLoging extension adds middleware for streamlined request logging. Instead of writing HTTP request information like method, path, timing, status code and exception details in several events, this middleware collects information during the request (including from Serilog.IDiagnosticContext), and writes a single event at request completion.

  • In Program.cs file, add the following code:
var app = builder.Build();
app.UseSerilogRequestLogging();
  • In MinimumLevel.Override section add "Microsoft.AspNetCore": "Warning":
"MinimumLevel": {
  "Default": "Information",
  "Override": {
    "Microsoft": "Error",
    "Microsoft.AspNetCore": "Warning",
    "System": "Error"
  },

Last but not least here I want to mention that you can override the Serilog setting by the Docker environment variable. Consider the following configuration:

"Serilog": {
  "MinimumLevel": {
    "Default": "Information",
    "Override": {
      "Microsoft": "Error",
      "System": "Error"
    },
    "Using": [ "Serilog.Sinks.MSSqlServer" ]
  },
  "WriteTo": [
    {
      "Name": "MSSqlServer",
      "Args": {
        "connectionString": "",
        "tableName": "Logs",
        "autoCreateSqlTable": true
      }
    }
  ]
}

Now in the docker-compose file, we want to pass the actual connection string:

my-api:
    environment:
      - ASPNETCORE_ENVIRONMENT=Production
      - Serilog__MinimumLevel__Default=Warning
      - Serilog__WriteTo__0__Args__connectionString="Your connection string"

The value of each section can be accessed by __, for instance, Serilog__MinimumLevel__Default is equivalent to:

"Serilog": {
  "MinimumLevel": {
    "Default": "",

In a section to access an item inside the array, use the item index number. "WriteTo" section accepts an array of sinks configuration. If you are using two sinks use Serilog__WriteTo__0__ to access the first sink and Serilog__WriteTo__1__ to access the second sink.

Let’s do a simple test. Open a CMD or Powershell at the project directory:

  • Type dotnet add package Serilog.Sinks.File to install File sink
  • Open appsettings.josn file and change the logging configuration like this:
"Serilog": {
  "MinimumLevel": {
    "Default": "Debug",
    "Override": {
      "Microsoft": "Information",
      "System": "Information"
    }
  },
  "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
  "WriteTo": [
    { "Name": "Console" },
    {
      "Name": "File",
      "Args": {
        "path": "log.txt",
        "rollingInterval": "Day"
      }
    }
  ]
}
  • Type dotnet build then dotnet run
  • After running the application you should see a log file inside the project directory

The source code for this walkthrough could be found on GitHub.