Add logging to ASP.NET Core using Serilog - .NET6

Logs are important, and Serilog makes it easy. Unfortunately, not all log records are created equal, resulting in a more challenging time for developers to debug an application. In a world where software development is crucial for the world to be fully functioning, logging often takes a backseat to unit testing and documentation.

However, logging is a very useful/powerful tool for debugging in production environments, and it provides the backend/ops teams vital data on the usage of the applications by the clients. When an application stops working or a feature suddenly returns errors or starts to crash, the logs are the place to start troubleshooting to find a quick fix for the issue(s).

In this tutorial, I will teach how easy it is to implement logging in ASP.NET Core using Serilog in an ASP.NET Core Console Application. You can of course adapt this into an API, etc... If you are ready, let's log some data from our .NET Apps to the console and a file.

What is Serilog?

Serilog is an easy-to-set-up-and-use logging library for .NET with a very clear API. Serilog adds support for structured logging, which makes it easier to troubleshoot an application.

GitHub - serilog/serilog: Simple .NET logging with fully-structured events
Simple .NET logging with fully-structured events. Contribute to serilog/serilog development by creating an account on GitHub.

With Serilog you get the option to log to multiple targets like:

  • Files
  • The Console
  • An email
  • Elasticsearch
  • Amazon
  • Azure
  • Datadog, and many other "sinks"/options

I always recommend Serilog no matter what size the application is. You can use Serilog in the most simple tiny application and the large complex enterprise solutions.

Prerequisites

All you need to follow along in this tutorial are the following:

# 1 - Create a Console Application

The first thing we have to do is create a new project. The project template I will be using for this tutorial is based on a simple .NET Core Console Application in Visual Studio.

visual studio, serilog, logging in asp.net core, serilog logging
Create new Console Application (.NET 6) macOS

# 2 - Install Dependencies

Before we start implementing code in our application we have to install some dependencies. We can install new dependencies in Visual Studio in various ways. I always prefer to use the Package Manager Console, but you can also do it through the dotnet-CLI or by using the built-in UI package manager in Visual Studio.

You can open up the NuGet Package Manager by clicking Tools --> NuGet Package Manager --> Package Manager Console to bring up the console. Inside the console, you have to run each of the below install commands for our Serilog logging project.

Install-Package Serilog
Install-Package Serilog.Sinks.Console
Install-Package Serilog.Sinks.File

Alternatively, you can use the built-in package manager, as shown below.

nuget, package manager, visual studio, serilog, sinks, serilog logging, serilog packages
Install Dependencies in Application through NuGet Package Manager

That's it - let's move on and create a new logger for our demo project.

# 3 - Create a new logger

With the dependencies in place, we can now move on to the part where we begin to implement the Serilog logger in our .NET application. Open Program.cs and add the following code to get started with the bare minimum for Serilog to work.

using Serilog;
using Serilog.Events;
using Serilog.Formatting.Json;

Log.Logger = new LoggerConfiguration()

    // Add console (Sink) as logging target
    .WriteTo.Console()

    // Set default minimum log level
    .MinimumLevel.Debug()

    // Create the actual logger
    .CreateLogger();

Console.WriteLine("Hello, World!");

Log.CloseAndFlush();

The code is quite self-explanatory. We add a new logger with a logging configuration for our Console and set the minimum level of logs to Debug().

You can configure Serilog in six different levels for logging. Below is a list with a short explanation:

  1. Fatal - Is used for reporting errors that force the application to shut down.
  2. Error - Is only used for logging serious problems that occurred while executing some code in your program.
  3. Warning - Is used when you have to report a non-critical event. This could also be a warning about unusual behavior in the application.
  4. Information - The information level is used when you got informative messages from events in a program. This could be logs about step completion in a program or when a user is signed in. Typically a system administrator loves this kind of log level - especially when they are delivered to a Syslog Server (Yeah I have been in that chair too... I know what I am talking about 😅).
  5. Debug - Debug messages are used to extend the information level when processing data in your application.
  6. Verbose - It's in the name. The verbose level is the noisiest level. I only activate this kind of log when I have to troubleshoot an application.

If you don't specify a level inside the LoggerConfiguration(). The Information level will be used by default. As you can see it's very easy to get started implementing Serilog in your .NET Apps.

# 4 - Extend the logger configuration

I promised you to log files both to the console and into a file. To log files into a file, we need to enable that option in our LoggerConfiguration().

Serilog can be configured in two ways. You can either do it using a configuration file or programmatically. I will show you both methods in this tutorial. Personally, I prefer doing it through a configuration file because I can change the logging behavior dynamically without having to update my Program.cs file for each environment.

Extend Logging configuration programmatically

The first option is the one you can go with if you prefer the programmatically way. The below code will add the following configuration to our logging config.

  • Write two different log files. The first one will include warnings and logs based on higher severity.
  • The second log-to-file configuration will create a new log file each day and add all logs with warning levels.
  • By default, all logs from the application with debug level will be shown in the console at runtime.
using Serilog;
using Serilog.Events;
using Serilog.Formatting.Json;

Log.Logger = new LoggerConfiguration()

    // Add console (Sink) as logging target
    .WriteTo.Console()

    // Write logs to a file for warning and logs with a higher severity
    // Logs are written in JSON
    .WriteTo.File(new JsonFormatter(),
        "important-logs.json",
        restrictedToMinimumLevel: LogEventLevel.Warning)

    // Add a log file that will be replaced by a new log file each day
    .WriteTo.File("all-daily-.logs",
        rollingInterval: RollingInterval.Day)

    // Set default minimum log level
    .MinimumLevel.Debug()

    // Create the actual logger
    .CreateLogger();

Console.WriteLine("Hello, World!");

Log.CloseAndFlush();

Configure Serilog using appsettings.json

Create a new file named appsettings.json and appsettings.Development.json in the root of your project. Add the following code inside appsettings.json:

{
  "Serilog": {
    "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
    "MinimumLevel": "Debug",
    "WriteTo": [
      {
        "Name": "Console",
        "Args": {
          "outputTemplate": "[{Timestamp:HH:mm:ss} {SourceContext} [{Level}] {Message}{NewLine}{Exception}"
        } 
      },
      {
        "Name": "File",
        "Args": {
          "path": "Logs/logs.txt",
          "outputTemplate": "[{Timestamp:HH:mm:ss} {SourceContext} [{Level}] {Message}{NewLine}{Exception}",
          "formatter": {
            "type": "Serilog.Formatting.Compact.CompactJsonFormatter, Serilog.Formatting.Compact"
          }
        }
      }
    ],
    "Enrich": [
      "FromLogContext",
      "WithMachineName",
      "WithThreadId"
    ],
    "Properties": {
      "Application": "Serilog Demo"
    }
  }
}

For the appsettings.Development.json file, you just add Logs/test-logs.json instead of the default Logs/logs.json value in the path for File.

Open Program.cs and replace it with the following code.

using Microsoft.Extensions.Configuration;

using Serilog;
using Serilog.Core;
using Serilog.Events;
using Serilog.Formatting.Json;

var configuration = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json")
        .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", true)
        .Build();

var logger = new LoggerConfiguration()
	// Read from appsettings.json
    .ReadFrom.Configuration(configuration)
    // Create the actual logger
    .CreateLogger();

logger.ForContext<Program>().Information("Hello World");
logger.ForContext(Constants.SourceContextPropertyName, "TWC").Warning("Hola, I'm a Warning!");
logger.ForContext(Constants.SourceContextPropertyName, "Microsoft").Error("Hello. This is Microsoft greeting!");


Log.CloseAndFlush();

By default, Serilog will read from the Serilog section in the configuration file. If you want to, it's possible to change the default value to another section, by doing the below operation:

{
  "TwcSection": {
    ...
  }
}
var logger = new LoggerConfiguration()
    .ReadFrom.Configuration(configuration, sectionName: "TwcSection")
    .CreateLogger();

Add extra dependencies for the new configuration to work

Install-Package Serilog.Enrichers.Environment
Install-Package Serilog.Enrichers.Thread
Install-Package Microsoft.Extensions.Configuration.Json
Install-Package Serilog.Settings.Configuration

If you run the application now, your bin folder will contain the Logs folder with your logs inside. They will be appended each time you start the application. Also, remember to check the console to see the different errors shown. Let's test it together.

Testing

We have now configured our logger and everything should be in place for some very helpful and structured logs should show up in the console and log files. Let's very that our code works.

From the console

This is the output in my console when running the above Program.cs file.

[00:38:31 Program [Information] Hello World
[00:38:31 TWC [Warning] Hola, I'm a Warning!
[00:38:31 Microsoft [Error] Hello. This is Microsoft greeting!

From the Logs/logs.json file

I just ran the program a few times to verify that the logs were appended to the logs.json file. This is my output:

[00:17:48  [Error] Hello World!
[00:18:28  [Error] Hello World!
[00:22:44  [Information] Args: []
[00:23:23 Program [Information] Hello World
[00:24:29 Program [Information] Hello World
[00:24:29 TWC [Warning] Hola, I'm a Warning!
[00:25:37 Program [Information] Hello World
[00:25:37 TWC [Warning] Hola, I'm a Warning!
[00:41:48 Program [Information] Hello World
[00:41:48 TWC [Warning] Hola, I'm a Warning!
[00:41:48 Microsoft [Error] Hello. This is Microsoft greeting!
[00:41:53 Program [Information] Hello World
[00:41:53 TWC [Warning] Hola, I'm a Warning!
[00:41:53 Microsoft [Error] Hello. This is Microsoft greeting!

As you can see, everything is nicely formatted using the format we specified in the configuration file for Serilog in .NET.

"outputTemplate": "[{Timestamp:HH:mm:ss} {SourceContext} [{Level}] {Message}{NewLine}{Exception}"

Summary

Adding proper logging to your application can help you in troubleshooting issues and errors in your application. Logs are not only useful in development but indeed also in production, just remember to change the level of information you need.

Logs can (for some) be a daunting task to do, but trust me... they are worth it. By using Serilog in ASP.NET Core, we can easily add logging that always shows up in the same structured way. If you rely on the configuration files, you will be able to skip creating files when testing development and only create them in releases. You can also change the level of logs produced this way and never forget to update the value when you deploy to production.

In this tutorial about implementing Serilog in .NET you learned about adding a professional logging system that provides a clear API for logging and got a lot of targets for where to ship your logs to. If you got any questions, please let me know in the comments below. Until next time, happy coding! ✌️

Source Code

The full source code is available for free on my TWC project on GitHub.

GitHub - Tech-With-Christian/Logging-Using-Serilog: This repository contains code examples on how you can implement logging in an ASP.NET Core Application using Serilog.
This repository contains code examples on how you can implement logging in an ASP.NET Core Application using Serilog. - GitHub - Tech-With-Christian/Logging-Using-Serilog: This repository contains ...
You've successfully subscribed to Tech with Christian
Great! Next, complete checkout to get full access to all premium content.
Error! Could not sign up. invalid link.
Welcome back! You've successfully signed in.
Error! Could not sign in. Please try again.
Success! Your account is fully activated, you now have access to all content.
Error! Stripe checkout failed.
Success! Your billing info is updated.
Error! Billing info update failed.