Serilog in ASP.NET Core 3.1 â Structured Logging Made Easy
In this article, letâs go through Serilog in ASP.NET Core 3.1 and itâs implementations. Now by default, ASP.NET Core comes with some basic logging features built-in. You must have seen the ILogger interface throughout your ASP.NET Core Application. But what if we want more control over how and where to log the details? That is where Logging Frameworks / Libraries come into play. Out of all of them, Serilog is one of the most popular Libraries for ASP.NET Core Applications. You can find the complete source code of the implementation here.
When I got to know about Serilog, there were no proper guides/tutorials to implement the same in my applications. For such an awesome Library like Serilog, itâs a shame to not have a well documented in-depth guide for the developers (Serilog documentation is great, but I feel that it may be overkill to many). This is the primary reason for me to write this article / in-depth guide. By the end of this article, I assure you that you will have close to complete knowledge of Serilog. Here are the topics that I will cover with this article.
What is Serilog?
Serilog is a third-party logging library that plugs into the default ILogger of our application with its own implementations. It enables the developers to log the events into various destinations like console, file, database, and more. Now, if you are already using a database in your ASP.NET Core Application, logging events to a database can be a good option. Serilog supports structured logging, which allows more details and information about the event to be logged. With structured logging in place, you could use these logs to debug in a very logical way.
Setting up the ASP.NET Core 3.1 Project
For this demonstration, letâs implement Serilog on an ASP.NET Core 3.1 WebApplication (Razor Pages). Since our focus is on logging and understanding various related concepts, we will keep the project setup simple and straight-forward. I will be using Visual Studio 2019 Community as my IDE.
Logging with the Default Logger
As I had mentioned earlier, ASP.NET Core applications ship with a default built-in logging system which includes some basic logging functions. To understand logging, letâs see how the basic logger works. Once you have created your WebApplication solution, navigate to Pages / Index.cshtml / Index.cshtml.cs. You can see the contractor injection of the ILogger interface. This is the default logger from Microsoft.
In the OnGet method of the IndexModel, letâs add a way to demonstrate loggin and also use the try-catch block . Here I will throw a Dummy Exception so that we can understand logging better. Also note that we will not be changing anything on the class further in this demonstration.
public void OnGet() { _logger.LogInformation("Requested the Index Page"); int count; try { for(count = 0;count<=5;count++) { if(count==3) { throw new Exception("RandomException"); } } } catch (Exception ex) { _logger.LogError(ex,"Exception Caught"); } } }
The OnGet method is fired every time you request for the Index Page (Home Page). So, as the code suggests, I am logging a message that says âRequested the Index Pageâ every time you request for this page. After that it runs a loop 5 times, and if the iteration count is 3, it throws a dummy exception âRandomExceptionâ which in turn gets caught in the catch block. This is logged as an error. This way, we have a function that mimics a practical production level function.
Before testing the logging, letâs switch to the Kestrel web server from IIS Express on Visual Studio.
What is Kestrel Webserver?
Kestrel is an open-source web server that ships by default with ASP.NET Core applications. We specifically need the Kestrel server for this demonstration because it opens up a console that contains all the logged events.
How to switch to Kestrel Webserver?
By default, you might have IIS Express as the selected server. You can switch to Kestrel Web Server by hitting the dropdown icon beside the IIS Express and choose the option with your application name. In our case, it is Serilog.WebAppliction. I will choose it and run the application using Control + F5 (I am starting the application without debugging to save some time here).


When you run your application, a console opens up along with your web application. In this console, you see certain logs from the application. In the end, we see our custom Log Messages and Exceptions (I have highlighted our concerned messages with yellow). Try to refresh the page on your web browser, you will see another set of similar messages on the console. This is the default logging provided with your application.
Log Levels
I also wanted you to know about the various Logging Levels. This is the fundamental concept of logging. When we wrote â_logger.LogInformation(âRequested the Index Pageâ);â, we mentioned to the application that this is a log with the log-level set to Information. Log levels make sense because it allows you to define the type of log. Is it a critical log ? just a debug message? a warning message?
There are 7 log-levels included :
- Trace â Detailed messages with sensitive app data.
- Debug â Useful for the development environment.
- Information â General messages, like the way we mentioned earlier.
- Warning â For unexpected events.
- Error â For exceptions and errors.
- Critical â For failures that may need immediate attention.
Note that Serilog may or may not have the same names for each level, but you get the idea, right? You can read more about log levels here.
Default Log Settings
The default settings for our logger is mentioned in appsettings.json. These settings allows you to define on what level of logs you need from a particular component. For example, any log messages that is generated by the Application (Microsoft) with levels Warning and above is logged to the console. This is the basic idea of log settings.
"Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }
With that out the way, letâs start the actual implementation of Serilog in our ASP.NET Core application.
Serilog Enrichers
To enable Structured Logging and to unleash the full potential of Serilog, we use enrichers. These enrichers give you additional details like Machine Name, ProcessId, Thread Id when the log event had occurred for better diagnostics. It makes a developerâs life quite simpler. We will use the enrichers later in this guide.
Serilog Sinks
Serilog Sinks in simpler words relate to destinations for logging the data. In the packages that we are going to install to our ASP.NET Core application, Sinks for Console and File are included out of the box. That means we can write logs to Console and File System without adding any extra packages. Serilog supports various other destinations like MSSQL,SQLite, SEQ and more.
Implementing Serilog in ASP.NET Core 3.1
Letâs start implementing Serilog in our ASP.NET Core 3.1 Application and make it the default logger application-wide. Here is a quick step-by-step guide on How to use Serilog in ASP.NET Core Applications.

Installing the Required Packages
For now, these are the packages that you require. Install them via the NuGet Package Manager or Console.
Install-Package Serilog.AspNetCore Install-Package Serilog.Settings.Configuration Install-Package Serilog.Enrichers.Environment Install-Package Serilog.Enrichers.Process Install-Package Serilog.Enrichers.Thread
Package #1 contains the core components of Serilog. This is an ASP.NET Core version of the package which comes along with additional features for our application.
Package #2 allows Serilog to read the settings from our configuration file, ie appsettings.json.
Package #3,4,5 are the enrichers that get details of the environment, process, thread, etc.
Now that we have installed all the necessary Serilog packages, letâs go ahead and configure Serilog.
Configuring Serilog in ASP.NET Core Applications
Our intention is to use Serilog instead of the default logger. For this, we will need to configure Serilog at the entry point of our ASP.NET Core Application, ie, the Program.cs file. Navigate to Program.cs and make the following changes.
public static void Main(string[] args) { //Read Configuration from appSettings var config = new ConfigurationBuilder() .AddJsonFile("appsettings.json") .Build(); //Initialize Logger Log.Logger = new LoggerConfiguration() .ReadFrom.Configuration(config) .CreateLogger(); try { Log.Information("Application Starting."); CreateHostBuilder(args).Build().Run(); } catch (Exception ex) { Log.Fatal(ex, "The Application failed to start."); } finally { Log.CloseAndFlush(); } }
Explanation.
Line #4,5,6 reads a config file (appsettings.json) and get the Configuration object for later use.
Line #9,10,11 Initiliazlies the Serilog using the settings from appsettings.json
Line #23 allows the logger to log any pending messages while the application closes down.
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .UseSerilog() //Uses Serilog instead of default .NET Logger .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
Line #3 makes the application use Serilog instead of the default Logger.
NOTE â It is important to note that, in this tutorial I am showing you a cleaner way to implement Serilog. There are possibilities to define the Serilog Configuration in Code (C#). But the issue is, you can not modify these settings at runtime. Hence it is a better practice to define these settings in appsettings.json.
Now that our Application is configured to support Serilog as the default logger, letâs define Serilog Settings in our appsettings.json.
Setting up Serilog
Navigate to appsettings.json and remove the default logging settings and replace it with the following.
{ "AllowedHosts": "*", "Serilog": { "Using": [], "MinimumLevel": { "Default": "Information", "Override": { "Microsoft": "Warning", "System": "Warning" } }, "WriteTo": [ { "Name": "Console" }, { "Name": "File", "Args": { "path": "D:\\Logs\\log.txt", "outputTemplate": "{Timestamp} {Message}{NewLine:1}{Exception:1}" } } ], "Enrich": [ "FromLogContext", "WithMachineName", "WithProcessId", "WithThreadId" ], "Properties": { "ApplicationName": "Serilog.WebApplication" } } }
Explanation.
This is the Settings for Serilog defined in appsettings.Json. As I had mentioned earlier, from now on, we will be only changing the settings here and wont touch C# code.
Line #3 marks the beginning of the Serilog Settings.
Line #6 to #13 defined the minimum level of logging for various and default components. You can see that by default, we log all the levels above Information Log Level. But for specific components like Microsoft, we need to log only for the Warning and above levels, so that we donât have a trillion lines of log in our sinks.
Line #14 to 25 marks the Serilog Sink Settings. For now, we have written the settings for File and Console Sinks only. We will extend it further in this guide.
Line #22 is where you can define the template of the log output.
Line #26 to 31 defines the enrichers for Serilog to provide more details.
Line #32 to 34 is where we can define custom properties that will appear in our structured log data.
Hope this part is clear. Letâs move forward and run the application. Make sure Kestrel is your default webserver.
Logging to Console with Serilog.
We do not need any extra changes to log to the console. You can see that our log is now much cleaner and to the point.

Logging to File with Serilog.
Now, letâs check the folder that we had defined earlier. We can see a new log.txt file created by Serilog.

Ok, We have talked so much about Structured Logging and Enrichers. But where are they? Console and File(text files) Sinks donât support Structured Logging. Letâs move to the next schema to achieve structured logging.
Structured Logging with Serilog.
To enable structured logging with the File Sink, we need to add a JSON formatter as a Parameter to the Settings. Letâs add new Sink to our appSettings.json/Serilog. Add the below code as the sink.
{ "Name": "File", "Args": { "path": "D:\\Logs\\structuredLog.json", "formatter": "Serilog.Formatting.Json.JsonFormatter, Serilog" } }
Line #2, this is File Sink.
Line #4 defines the path of the JSON File.
Line #5 seats the JSON Formatter of Serilog to enable structured logging.
Now, just run the application again. Navigate to the defined path, you will see a new JSON file. Open this file with a Code Editor that can format JSONs, so that it is easier to read through the data. I used Visual Studio Code to open the log file. Here is a screenshot of the log.

This is the Structured Log of a single log Event, the Error event. You can see the amount of data given to us by Serilog. Also note that we have the Machine Name, ProcessId, and many details that can help debug the application in the longer run. Our Custom Property, ApplicationName also appears in our Log File. Pretty cool, yeah?
Logging to Database with Serilog.
Now, this is what you would probably want to use for applications at production environment. It makes much more sense to log into a relational database, so that , at a later point of time, you could use some queries to inteligently fetch the log details by AppliationName,LogLevel, etc.
In this tutorial, I will show you the way to log to Microsoft SQL Server Database. For this, we need to install an additional package (a new Sink!). It is possible to log to multiple databases as well (you will have to install the specific Serilog Sink package).
Install-Package Serilog.Sinks.MSSqlServer
{ "Name": "MSSqlServer", "Args": { "connectionString": "<your connection string / named connection Here>", "sinkOptionsSection": { "tableName": "Logs", "schemaName": "EventLogging", "autoCreateSqlTable": true }, "restrictedToMinimumLevel": "Warning" } }
Line #2 defined the sink as MSSQLServer Sink.
Line #4 is where you would want to put in the connection string / named connection string for the logs to be inserted.
Line #6 the table name.
Line #7 the name of the schema.
Line #8 is a cool feature where Serilog creates the table for you if it does not exist.
Line #10 restricts the level of minimum log level. We do not want to log everything on to the database during production. Just the Errors and Fatal Logs are Enough.
Let me fire up the application. It may take a few more seconds than usual because Serilog is setting up your database in the background. After the application executes. Open up SQL Management Studio and check the database mentioned in your connection string. You will find a new Table âEventlogging.Logsâ. This is the one created by Serilog. Run a Select * Command on this table.

Note that our MSSqlServer sink only logs the events which are high priority, ie, Errors / Exceptions / Fatals. This can be set for any sink as well depending on your requirement.
Great! Now we have learnt a clean way to implement Serilog / Logging in your ASP.NET Core application.
Summary
In this article, we have gone through with basics of logging, the behavior of default .NET logger, various concepts of logging, Serilog library, and itâs implementation, and different Serilog Sinks. What is your favorite Logging Framework? Is it not Serilog ? Well, mine is. In the next section you can find the link to the source code that I demonstrated in this article. Leave behind your queries , suggestions in the comment section below. Also, if you think that you learnt something new from this article, do not forget to share this within your developer community. Happy Coding!
Source Code
Find the completed source for Serilog implementation in .NET Core 3.1 here.
Do Follow me on GitHub as well.
Frequently Asked Questions
What are the best Logging Frameworks for ASP.NET Core Applications?
Serilog and Nlog are by far the most popular and better logging frameworks for ASP.NET Core Applications.
Hi Mukesh,
Thank you very much for your nice Article. I really learned a lot with this article. One update to you:
You cannot create your project name as Serilog.WebApplication. If you do like this, Serilog class will be overridden with âSerilogâ in your project file name. Most of the functionality wonât work.
Rest everything is really nice.
Hi, Thanks for the feedback.
Yes, I get it. However this is just a demonstration. I use the naming to maintain my github repos, so that it is easier to navigate. But thanks for the info.
Regards
df
Hi Mukessh,
Text based blogs really kill lot of time to learn and understand. Do you have video based posts?
I will join you if you have video based posts.
As of now, I do not have an youtube channel. But I will start it quite soon as many are asking the same. It actually takes a lot of time and work for Youtube. Will Update you
this is really very helpfull most of the peoples, i learned alot from you thanku
Thanks to make our life easier by your efforts.
Thanks a lot for the feedback! There are quite a few other articles as well. Do go through them.
Thanks and Regards.
Thanks a lot this is the first time I knew about serilog, I always use NLog
Serilog is much newer than NLog, although both are great for logging.
Thanks for the feedback.
Regards
Hi Mukesh,
Thanks a lot, I am able to implement serilog following the blog. But I am trying to make exception logs as global , some how I am not able achieve this. How we can do that, any idea ..
great job
Can we name the file with {yyyy-MM-dd}.txt format?
Can serilog used without newtonsoft dependency in .net core 3.1 and newer upcoming version?
Hello!
Awesome well-explained article. I was able to get up and coding in no time. A few hiccups I faced in this post:
The subtitle of your article: âby Mukesh Murugan | Updated on May 27, 2020 | ASP.NET Coreâ
â The hyper link on you name links to an invalid website
â The âGet the Codeâ button in the end doesnât work. It just reloads the page
â None of you about or author pages have a link to your GitHub
For those looking: https://github.com/iammukeshm/Serilog.WebApplication
Hey! Oops. Never Noticed. Thanks a lot. All the links are fixed. Thanks again
Regards
Hi Mukesh
Thank you very much for your great Article. I really learned a lot .
Hello Mukesh,
Itâs typically rare that I post a comment on-line but I felt compelled to send you a note of thanks for this and your other topical development posts (I have several bookmarked for reference and recommended them to our development team). Iâve been coding for 25+ years now and I have to say that your style is refreshing and informative. I especially enjoy how well organized the information is presented. While it should be obvious that many of these topics vary in depth, these posts serve as a a great launching point, answering the fundamental question of âIâm interested in learning about X but how do I get started?â
Great job!
Thanks a lot for your feedback đ Itâs quite awesome when it comes from a much more experienced person. Thanks đ
Thanks, thatâs much appreciated, but sometimes this old dog feels like technology is moving at such blistering speeds that itâs impossible or even foolish to try to keep upâdepends on the day, perhaps! Itâs great to have such a rich development community to tap into when tackling something completely new, though, so thanks again for sharing as itâs helped jumpstart integrating some of these cool technologies. Personally I think the biggest challenge Iâve encountered in ASP.Net Core is knowing which package(s) to install!!
Happy coding!
You are not alone Jeff, it is infact moving way too fast, especially the web/mobile applications space⊠but i guess eventually it would stabilize with the advent of .NET 6 + MAUI
this is very nice topic and clear explianation.
I wonder how do we implment logging like what happend to the system.
â What entitiy is created, updated, deleted, who did it, when, what kind of old and new data
Hi Musk,
It is logging to database from the program class.
But when i try to log from api controller class. nothing is logging
I need to remove this line
finally
{
Log.CloseAndFlush();
}
excellent mate đ
Great job,
strong & simple
Thanks
hello there nice post, but i need to elastic search in the cloud, how could i do it using this tutorial? thanks
Hi Mr. Mukesh
The serilog can also be used without changing its configuration.
So I want to leave the appsettings.json file a little bit.
By setting writeTo Mssql, ârestrictedToMinimumLevelâ: âWarningâ working fine.
But when setting ârestrictedToMinimumLevelâ: âCriticalâ it has error.
â> System.ArgumentException: âRequested value âCriticalâ was not found.â
Thanks Mukesh â You have shown that one can grow more while helping others grow too, keep up your blog posts. Even though there are tons of materials available online, very few (with real IT experience) discuss it in a well-structured manner like you do. Moreover, you also discuss some best practices/ patterns for creating industry grade applications.
Want to buy a coffee for you, but the payment page does not respond post entering credit card details, the buy spinner keeps spinningâŠ