Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET / ASP.NET-Core

Logging with Serilog in ASP.NET Core Web API

4.86/5 (7 votes)
17 Oct 2022CPOL6 min read 35.7K  
In this tutorial, we will learn how to implement logging with Serilog in ASP.NET Core Web API.
In this tutorial, we will learn how to implement logging with Serilog in ASP.NET Core Web API, we will be using the Serilog library to write logs into rolling files and see how can we inject the logging instance into different classes across the API Project to help us start writing logs. We will also learn how to write our own custom Sink.

Logging is one of the most important and critical features in development that allows the developers and product owners to troubleshoot and analyze application errors. Using Serilog or any other logging provider, whenever your application writes comprehensive and extensive logs, you will be able to trace any error or issue that happens and that will greatly help in finding the solution for the problem.

In this tutorial, we will learn how to implement logging with Serilog in ASP.NET Core Web API.

It is pretty easy and straightforward to introduce logging with Serilog in ASP.NET Core Web API. In fact, there is a specific NuGet package for Serilog that is tailored for the ASP.NET Core Projects.

So let’s start with the tutorial.

Create ASP.NET Core Web API Project

Open Visual Studio 2022, and create a new project in ASP.NET Core Web API. Give it a name like: LoggingWithSerilog.

Create project

Choose .NET 6 and press on Create.

Create project - choose .NET Core framework

Once the template project is loaded, right click on the project name from the solution explorer and choose Manage NuGet Packages, search for Serilog, then choose to install Serilog.AspNetCore.

Install Serilog Nuget

This NuGet package bundles with it dependencies for the Serilog sinks which include Debug, Console and File. For this tutorial, we are mainly interesting in File sinks since we will be dumping our logs within rolling files.

Serilog Configurations

Now open your appsettings.json file and add the below section:

JSON
"Serilog": {
    "Using": [ "Serilog.Sinks.File" ],
    "MinimumLevel": {
      "Default": "Debug",
      "Override": {
        "Microsoft": "Error",
        "System": "Debug"
      }
    },
    "Enrich": [ "FromLogContext", "WithMachineName", "WithProcessId", "WithThreadId" ],
    "WriteTo": [
      {
        "Name": "File",
        "Args": {
          "path": "C:\\Web Apis\\Logs\\LoggingWithSerilog\\RestApiLog.log",
          "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} 
           [{Level}] [{SourceContext}] [{EventId}] {Message}{NewLine}{Exception}",
          "rollOnFileSizeLimit": true,
          "fileSizeLimitBytes": 4194304,
          "retainedFileCountLimit": 15,
          "rollingInterval": "Minute"
        }
      }
    ]
  }

In your Program.cs file, add the following code:

C#
var logger = new LoggerConfiguration()
        .ReadFrom.Configuration(builder.Configuration)
        .Enrich.FromLogContext()
        .CreateLogger();
builder.Logging.ClearProviders();
builder.Logging.AddSerilog(logger);

Now, to be able to write logs from within the controller, we need to use the logger instance injected to the Controller’s constructor.

Open the WeatherController, in the constructor, you will notice there is already an injected parameter for logger.

C#
private readonly ILogger<weatherforecastcontroller> logger;
public WeatherForecastController(ILogger<weatherforecastcontroller> logger)
{
    this.logger = logger;
}

Now in this endpoint GetWeatherForecast, replace the template code with the below lines:

C#
logger.LogDebug("Inside GetWeatherForecast endpoint");
var response = Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
    Date = DateTime.Now.AddDays(index),
    TemperatureC = Random.Shared.Next(-20, 55),
    Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
logger.LogDebug($"The response for the get weather forecast is 
{JsonConvert.SerializeObject(response)}");
return response;

Notice that JsonConvert is a class used from Newtonsoft.json package, so if you do quick action or Ctrl+. , you will get the menu that will suggest to install the needed NuGet package, you can choose to install it:

Install Newtonsoft.Json

Now we have the Serilog set up and ready to start writing the logs, let’s run a quick test to see if it works.

Press F5, the default browser will show up and render the Swagger UI page of your API.

Running Page of Swagger UI for ASP.NET Core Web API

Logging Exceptions

The most important use case of logging is in tracing and troubleshooting errors and exceptions, once you log the exception details, it will be much easier for you to know when, where and why the error occurred, which will lead to a faster resolution of the problem, especially when such issue is on your production application.

To have a proper exception handling across your API project, it is recommended that you add a middleware that handles your exceptions and inside, you will inject the logger instance and implement the logging code.

This middleware will intercept any exception that will happen during the execution of your endpoints, will return a proper http status code with response and then it will log the exception details.

So in your project, let’s add a folder with name Middleware, and inside it, let’s create a new class with name ExceptionHandlingMiddleware.

C#
using Newtonsoft.Json;
using System.Net;
namespace LoggingWithSerilog.Middleware
{
    public class ExceptionHandlingMiddleware
    {
        public RequestDelegate requestDelegate;
        private readonly ILogger<ExceptionHandlingMiddleware> logger;
        public ExceptionHandlingMiddleware
        (RequestDelegate requestDelegate, ILogger<ExceptionHandlingMiddleware> logger)
        {
            this.requestDelegate = requestDelegate;
            this.logger = logger;
        }
        
        public async Task Invoke(HttpContext context)
        {
            try
            {
                await requestDelegate(context);
            }
            catch (Exception ex)
            {
                await HandleException(context, ex);
            }
        }
        private Task HandleException(HttpContext context, Exception ex)
        {
            logger.LogError(ex.ToString());
            var errorMessageObject = 
                new { Message = ex.Message, Code = "system_error" };
           
            var errorMessage = JsonConvert.SerializeObject(errorMessageObject);
            context.Response.ContentType = "application/json";
            context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
            return context.Response.WriteAsync(errorMessage);
        }
    }
}

This middleware will perform two tasks: handle any exception that might happen during the execution of any endpoint and return a generic response with error code and message and the second task it will log the exception that happened using the configured logging provider (Serilog).

Now the only thing left here is to inject the new middleware into the application’s pipeline, so open your program.cs file and let’s add the below line after the builder.Build() method call:

C#
app.UseMiddleware(typeof(ExceptionHandlingMiddleware));

Next, let’s make the GetWeatherForecast endpoint fail by throwing an arbitrary exception inside it:

C#
throw new Exception("Failed to retrieve data");

Run the application and test calling the Get WeatherForecast endpoint:

Swagger UI Response for API call

Now let’s see if Serilog worked and added the logging details into the File Sink:

Log files using Serilog

Open the file to see the dumped logs:

Serilog Log file details

Serilog Sinks

Serilog has the concept of sinks as plugins, comes bundled with numerous predefined sinks that you can configure and directly use to start writing logs.

Such sinks range from simple ones like Console or File to more advanced sinks like ElasticSearch or sinks that can directly connect and dump the logs to databases such as SQL Server, MongoDB, or to cloud logging services like Azure Analytics, Amazon CloudWatch or cloud databases such as Azure Cosmos DB or Aws Dynamo DB, and even you can find sinks that would dump the logs into the event log of windows and so many other options.

In this tutorial, we have learned how to connect the ASP.NET Core Web API with Serilog, you can experiment with different types of provided Serilog sinks.

Check the following link to view the full list of provided Serilog sinks.

Custom Serilog Sink

In some cases, you might need to write your own sink, Serilog enables you to achieve this by implementing the ILogEventSink in your own class and writing the needed custom logging code in the Emit method.

Let’s create a Custom Sink, add a folder with name Sinks, and create a new class with name CustomSink.

C#
using Serilog.Core;
using Serilog.Events;

namespace LoggingWithSerilog.Sinks
{
    public class CustomSink : ILogEventSink
    {
        public void Emit(LogEvent logEvent)
        {
            var result = logEvent.RenderMessage();

            Console.ForegroundColor = logEvent.Level switch
            {
                LogEventLevel.Debug => ConsoleColor.Green,
                LogEventLevel.Information => ConsoleColor.Blue,
                LogEventLevel.Error => ConsoleColor.Red,
                LogEventLevel.Warning => ConsoleColor.Yellow,
                _ => ConsoleColor.White,
            };
            Console.WriteLine($"{logEvent.Timestamp} - {logEvent.Level}: {result}");
        }
    }
}

In the above example, we are changing the console text color according to the level of the log.

To be able to configure our application to use the new Custom Sink, we must define an extension method for the new Custom Sink.

C#
using Serilog;
using Serilog.Configuration;

namespace LoggingWithSerilog.Sinks
{
    public static class CustomSinkExtensions
    {
        public static LoggerConfiguration CustomSink(
                  this LoggerSinkConfiguration loggerConfiguration)
        {
            return loggerConfiguration.Sink(new CustomSink());
        }
    }
}

Now, the last part is to glue to components together in the Program.cs, by introducing the new Custom Sink within the Serilog logging configuration.

C#
var logger = new LoggerConfiguration()
        .ReadFrom.Configuration(builder.Configuration)
        .WriteTo.CustomSink()
        .Enrich.FromLogContext()
        .CreateLogger();

As you can see above, we are using two sinks, one from configuration (File Sink), and the other is using the Custom Sink defined using the WriteTo Custom Sink Extension method.

Now let’s run and see the result on the console of the running:

Serilog Console with different colors according to logging level

And if you open the logs folder, you can still see the File Sink writing the logs into rolling files:

Serilog Log file

Summary

In this tutorial, we have learned how to do logging with Serilog in ASP.NET Core Web API. Also, we have learned how to write a middleware to handle all the exceptions and how to log them using Serilog.

Also, we got introduced to the different provided Serilog Sinks and we managed to create a new Custom Sink that would write to console and change the color according to the log level.

I hope this tutorial introduced you properly to Serilog. Please feel free to leave me comments and share the tutorial with your network and colleagues.

You can find the source code in my GitHub account.

Check out my other tutorials:

Bonus

Please enjoy this highly energetic but beautiful piece of music by L.V. Beethoven

Beethoven – Sonata No.17 Tempest 3rd Movement

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)