Skip to Main Menu

Exploring Serilog v2 - Configuration

This is the next post in a series re-discovering Serilog v2.

In the introduction to the Serilog Console Sink I mentioned about how you can use a number of ways to configure Serilog. Below is a simple guide on which approach you may like to take when setting up your configuration.

graph TD C(Serilog Configuration) -->| | Y{What do I want? } Y -->| Code | D(Fluent Interface) Y -->| A File | Z{What file type? } Z -->| | E(JSON) Z -->| | F(XML) E -->| | H(Serilog.Settings.Configuration) F -->| | J(Serilog.Settings.AppSettings)

The diagram is a little simple… so you may want to consider what other features are available. To take a closer look, checkout the following GitHub repositories.

Serilog.Settings.Configuration

This is the package to use if you are considering a JSON approach to configuration, for example in ASP.NET Core. Under the hood this package currently uses the Microsoft.Extensions.DependencyModel & Microsoft.Extensions.Configuration.Abstractions packages. The library works by infering the configuraion of key method overloads to setup Serilog. This includes:

  • Minimum Level of events to be logged
  • Enrichment of events
  • Filtering of events
  • Sink configuration
  • Audit Sinks

A Quick Example

Let’s create a quick console app, that logs to both the console and a file sinks whilst using a appsetting.json file for configuration.

  • dotnet new console -n example-console
  • cd example-console
  • dotnet add package Serilog.Settings.Configuration
  • dotnet add package Microsoft.Extensions.Configuration.Json
  • dotnet add package Serilog.Sinks.Console
  • dotnet add package Serilog.Sinks.File
  • dotnet add package Serilog.Enrichers.Process
  • touch appsettings.json
  • Open the Program.cs and add the following.
using System;
using System.IO;
using Serilog;
using Serilog.Events;
using Serilog.Core;
using Microsoft.Extensions.Configuration;

namespace example_console
{
    class Program
    {
        static void Main(string[] args)
        {
            // Read the appsettings file
            var configuration = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json")
                .Build();

            // Apply the config to the logger
            Log.Logger = new LoggerConfiguration()
                .ReadFrom.Configuration(configuration)
                .CreateLogger();
 
            Log.Verbose("This is a verbose statement");
            Log.Debug("This is a debug statement");
            Log.Information("This is a info statement");
            Log.Warning("This is a warning statement");
            Log.Error(new IndexOutOfRangeException(), "This is an error statement");
            Log.Fatal(new AggregateException(), "This is an fatal statement");
        }
    }
}
  • Then open appsettings.json and add the following setup.
{
    "Serilog": {
      "Using": ["Serilog.Sinks.Console"],
      "MinimumLevel": "Information",
      "Enrich": ["WithProcessId"],
      "WriteTo": [
        { "Name": "File", "Args": { "path": "serilog-configuration-sample.txt" } },
        {
        "Name": "Console",
        "Args": {
          "outputTemplate": "[{Timestamp} [{Level}] {Message} {Exception} {Properties} {NewLine}",
          "theme": "Serilog.Sinks.SystemConsole.Themes.SystemConsoleTheme::Literate, Serilog.Sinks.Console"
        }
      }],
      "Properties": {
        "Application": "Exploring Serilog v2"
      }
    }
  }

A couple of gotchas to remember.

  • Make sure your using/namespace declarations are correct, Sinks will not be discovered if there is a typo. e.g. "Using": ["Serilog.Sinks.Console", "Not A Sink Namespace"]
  • Ensure the args you pass to sinks are exposed via the fluent configuration. I have been bitten by slight inconsistencies in how Sinks are configured. If you are interested how some of the internals work, check out ConfigurationReader.

Serilog.Settings.AppSettings

If you are looking for a more traditional XML based approach, then check out Serilog.Settings.AppSettings. This library is the best option when using an <appsettings> block and uses a similar approach to the Serilog.Settings.Configuration package.

A Quick Example

Once again, let’s create a quick console app. Following the previous example this will also log to both the console and a file sinks however this time a example.config file will be targeted.

  • dotnet new console -n example-console
  • cd example-console
  • dotnet add package System.Configuration.ConfigurationManager
  • dotnet add package Serilog.Settings.AppSettings
  • dotnet add package Serilog.Sinks.Console
  • dotnet add package Serilog.Sinks.File
  • dotnet add package Serilog.Enrichers.Process
  • touch appsettings.config
  • Open the Program.cs and add the same following code.
using System;
using System.IO;
using Serilog;
using Serilog.Events;
using Serilog.Core;

namespace example_console
{
    class Program
    {
        static void Main(string[] args)
        { 
            var basePath = AppDomain.CurrentDomain.BaseDirectory;
            var configFile = Path.GetFullPath(Path.Combine(basePath, "appsettings.config"));

            // Apply the config to the logger
            Log.Logger = new LoggerConfiguration()
                .ReadFrom.AppSettings(filePath: configFile)
                .CreateLogger();
 
            Log.Verbose("This is a verbose statement");
            Log.Debug("This is a debug statement");
            Log.Information("This is a info statement");
            Log.Warning("This is a warning statement");
            Log.Error(new IndexOutOfRangeException(), "This is an error statement");
            Log.Fatal(new AggregateException(), "This is an fatal statement");
        }
    }
}
  • Then open .csproj and add the following to ensure the config file is copied.
  <ItemGroup>
    <None Update="appsettings.config">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
  </ItemGroup>
  • Finally open appsettings.config and add the following setup. Here we setup the console and file sinks along with a process enricher.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <appSettings>
    <add key="serilog:using:Console" value="Serilog.Sinks.Console" />
    <add key="serilog:write-to:Console"/>
    <add key="serilog:write-to:Console.outputTemplate" value="[{Timestamp} [{Level}] {Message} {Exception} {Properties} {NewLine}" />
    <add key="serilog:using:File" value="Serilog.Sinks.File" />
    <add key="serilog:write-to:File"/>
    <add key="serilog:write-to:File.path" value="serilog-configuration-sample.txt" />
    <add key="serilog:using:Process" value="Serilog.Enrichers.Process" />
    <add key="serilog:enrich:WithProcessId"/>
  </appSettings>
</configuration>

The is another new kid on the block I did forget to mention. It is early days, however using the lessons of logs past, this library attempts to unify the configuration of Serilog and onboard users more seemlessly.

Serilog.Settings.Combined

Get Amongst It!!

comments powered by Disqus