Hello Friends, another article. Asp.net core comes with logging capabilities, and this is great. Logging is so easy to use in ASP.net core that it is also easy to omit some important diagnostics features it comes with or use these powerful logging features in the wrong way.  In this article, I will guide you through the essentials of ASP.net core Structured Logging with Serilog and without Serilog too. This guide is what you need to grasp the core concepts quickly. So, let’s dive into these 4 Structured Logging Techniques in ASP.net core.

Quick Note about Basic Logging in ASP.net core

As I stated earlier, Asp.net core comes with a default logging framework, and it is configured by default when you create a web application builder. The default configuration adds console, debug, event source and event log as mentioned in the docs so you have nothing to do.

If you find this article useful, please follow me on Twitter,  Github, Linkedin, or like my Facebook page to stay updated.
Follow me on social media and stay updated

Using a logging provider (use case: Serilog)

Before diving into the core of this article, let’s talk about providers. Since we’re going to talk about both the default ASP.net core logging system and serilog. ASP.net core provides room to use other logging providers that will make your logging even better. These providers are powerful, and you should leverage their powerful features. In this article, we will use serilog as our provider.

Adding Serilog to your project

To configure serilog, it is easy. Add the nugget package “Serilog.AspNetCore” to your project. Once you add the nugget package, you have to configure serilog. In your program.cs (Dotnet 8+), use this code snippet to add console logging, to introduce serilog as default provider, and to load configurations from our appsettings file.

After doing this, we can add more configuration options in the appsettings.json file. For example, to set the log level in serilog, you could use the following:

To make this log level specific to a given logging context, or namespace, you could use the following. Note that this is very customizable. At the end of this article, I’ll add links to resources where you can learn more about these configuration options.

1-  Use scopes to delimit your logs properly.

What is a logging scope? Let me quote Microsoft’s definition “A scope can group a set of logical operations. This grouping can be used to attach the same data to each log that’s created as part of a set”.

Here, we will add scopes that span entire Http requests and add a user id or client id scope, thereby specifying every time which user or client of our API is performing what action. This clearly delimits the logs, even in lower inner level logs of the framework, and saves time typing the user id or client id in every log for the appropriate logs and makes code cleaner.  This is how we do it:

NB: Scopes are called in using statements because some resources need to be freed once the scope is done.

  • First, we must intercept http requests, check if the request is authenticated. If it is, we use the user’s id or the API client’s Id to define the scope. If it is not, then we just continue the request flow. To do this, we’ll need a middleware.
  • We must add our middleware to our project. NB: it should be added after the authentication middleware addition else; it will be called before authentication is made. And we don’t want that.

After doing this, your logs will be clearly delimited and this will facilitate diagnostics.

In case you use CQRS, here is a great article I wrote about CQRS Command Validation with mediatr in Asp.net core. I think this article will be very useful for any dev especially those that mix validation logic inside their Command and Query business logic.

If you find this article useful, please follow me on Twitter,  Github, Linkedin, or like my Facebook page to stay updated.

Follow me on social media and stay updated

2-  Leverage Structured logging capabilities: Writing Logs in JSON Format

Here is the 2nd of the structured logging techniques in ASP.net core. First, what is structured logging? This is the act of logging application events in a well structured and consistent format, making the logs easy to read, search and interpret. This is better than just logging strings in an output. Taking this into consideration, the best way to log efficiently is to give a structure to our logs. What better structure than JSON?

Configuring JSON format for your logs in ASP.net core

ASP.net core makes it easy to configure the output format of your log entries. Inside the appsettings.json file, you can use the “FormatterName” and “FormatterOptions” parametters to do just that. Here is an example:

In the example above, I specify that every console log will be in json format, be indented, include scopes etc… After specifying this, your logs will appear with the json format in the console.

Configuring JSON format for your logs with Serilog

Obviously, serilog allows you to give a specific structure to your logs. In our example, we will make our console logs appear in json format. Remember the code above that set’s up serilog ? you’ll just need to modify it a little bit :

.WriteTo.Console(formatter: new JsonFormatter())

Then, you can play with the output template to either only log json logs or add some other information to the log output. Here is how to make logs include the json message, a newline and then any potential exception:

3-  Using logging parameters properly and how to destructure them

Passing parameters when logging is very important, and it is very easy to do. All you need is to add curly braces with the name of your parameter and add it to the logging method. No need to use a “$” prefix to your strings. The logging framework already handles that. Example: the code below logs the user’s age.

Once this parameter is passed, if you enabled structured logging with Json, the output will be clearly written making it easy to query logs based on the user’s age param I passed. Here is part of the output we could have:

What about passing complex objects as parameters when logging? well, ASP.net core shows its limits here, since it only converts complex objects to strings using their “ToString()” methods. You won’t have json objects in your logs. But logging providers like Serilog permits us to convert these parameters to json objects this is known as destructuring.

To destructure a complex parameter to json in serilog, use “@” and to only display the parameter’s signification, or type, use the “$” sign. Here is an example.

The code above will produce a log entry with the Info parameter in a json format. With structured logging activated, it will look like this:

Now, if I use the $ sign for destructuring, as shown below:

I get the following output:

You can take this to the next level by fully customizing how your parameters are destructured, thanks to this open-source library https://github.com/destructurama/attributed which works with serilog. It allows you to hide sensitive data when destructuring objects, or mask the data with “***” in case it is a password for example.

To configure destructuring, in your appsettings.json, you can add custom configurations. Here is an example of configurations I added. At the end of this article, I added links you can follow to learn more about this feature.

4-  Don’t forget to specify the logging Category and Chose the Correct log level.

This is the easiest of the structured logging techniques in ASP.net core. As it is mentioned in the docs I referenced above, all you need is to inject an ILogger or ILogger<T> in your class where “T” is the category of log and type of the class. Once T is specified, all logs will contain a string with the name of the class associated to it. Here is an example:

 For example, if I log the following:

The output will contain the category and will be:

This is extremely useful when performing diagnostics. You want to know where your logs are from when an error occurs.

Specifying the log level is easy. Here (https://learn.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-7.0#configure-logging ), The docs do a great job in specifying how to do just that. But which log level should you choose? Here are the different log levels and what they mean.

  • Trace: The lowest log level, used to log the maximum amount of details possible. It contains a lot of information about the functioning of the system. You should write logs at this level when you want to add very detailed information about the inner functioning of your software. And in the configurations, this log level should NEVER be activated by default in production because way too many details are contained in it. You should activate it when trying to figure out complex issues that occurred in your software.
  • Debug: This is the second level from the bottom. As its name implies, it is used to log debug information and contains much information too. You should only use this in test environments. You could activate it in production to figure out when something went wrong, but make sure to rewind back to a higher level.
  • Information: This is used to send information about the usage of the software.
  • Warning: This is used in cases where your software behaves unexpectedly, but not in a way that will cause a failure in your system. For example, when something the user is querying is not found.
  • Error: Use this log level when failures occur in your system. Failures that are specific to a certain use case, or to a certain user. These failures should not be the kind that stops your system from working properly.
  • Critical: This is the final level. It is used when something goes wrong that prevents your whole system from working properly, for example, when you can’t access your database or when you run out of memory.

Conclusion

What do you think about these 4 structured logging techniques in ASP.net core? These are in my opinion, the essential notions you need to grasp when adding logging features to your ASPnet core app. With these, you’re sure to log like a pro and save a lot of debugging and diagnostics time. I hope this helps you in your software development journey. If it does, please share this article and follow me for more.  

References

https://crossprogramming.com/2021/12/23/structured-logging-in-asp-net-core-using-serilog-and-seq.html

https://github.com/serilog/serilog/wiki/Formatting-Output

https://github.com/serilog/serilog/wiki https://learn.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-7.0

Follow me on social media and stay updated

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.