Last Updated on June 21, 2023 by Aram
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 happen 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
Choose .NET 6 and press on Create.
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
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
"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:
1 2 3 4 5 6 |
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.
1 2 3 4 5 |
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:
1 2 3 4 5 6 7 8 9 10 |
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:
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.
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
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 2 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:
1 |
app.UseMiddleware(typeof(ExceptionHandlingMiddleware)); |
Next, let’s make the GetWeatherForecast endpoint fail by throwing an arbitrary exception inside it:
1 |
throw new Exception("Failed to retrieve data"); |
Run the application and test calling the Get WeatherForecast endpoint
Now let’s see if Serilog worked and added the logging details into the File Sink:
Open the file to see the dumped logs:
Serilog Sinks
Serilog has the concept of sinks as plugins, comes bundled with a 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 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 experiement 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
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
1 2 3 4 5 |
var logger = new LoggerConfiguration() .ReadFrom.Configuration(builder.Configuration) .WriteTo.CustomSink() .Enrich.FromLogContext() .CreateLogger(); |
As you can see above, we are using 2 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
And if you open the logs folder, you can still see the File Sink writing the logs into rolling files:
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
- Secure Angular Site using JWT Authentication with ASP.NET Core Web API
- Localization in ASP.NET Core Web API
- Google reCAPTCHA v3 Server Verification in ASP.NET Core Web API
- Apply JWT Access Tokens and Refresh Tokens in ASP.NET Core Web API 6
Bonus
Please enjoy this highly energetic but beautiful piece of music by L.V. Beethoven
Beethoven – Sonata No.17 Tempest 3rd Movement