Easy Modular Monolith — Part 3 — Logging (Serilog and Seq)

Norbert Dębosz
ITNEXT
Published in
5 min readSep 6, 2021

--

In a perfect world, in IT systems there is no place for bugs and maintenance. As developers, we prefer to deliver new stuff instead of maintaining old code and fixing bugs. But in most software systems, we will have to maintain something, fix some issue or change currently implemented logic.

How to make our life easier and allow us to do the above much faster?
Start from IMPLEMENTING LOGGING!

What is logging?

Logging is the action of storing information about an event that occurs in the system. The piece of information that is stored is called Log.
Logging is one of three pillars of observability alongside Tracing and Metrics.
It allows us to save a lot of time troubleshooting problems as it provides contextual data of an event.

Sample log from ModularMonolith application

What we should log?

The most crucial part of implementing logging is that it should be used mostly as a diagnostic tool.
Logging should never contain any sensitive data like passwords, tokens and everything that is considered private and confidential.

In our example we will focus on logging:

  • Requests flow — to be able to trace an execution flow.
  • Performance statistics — to troubleshoot slow requests.
  • Exceptions — for easier maintenance and bug fixing.

Implementation

Requirements:

  • Log information at least into two places — we should be able to see logs in console (for development environment) and in a file (for production environment).
  • We should be able to change the configuration from the appsettings.json file.
  • Implementation should be flexible and allow us to add more “sinks” (places to which we save a log) quite easily.

Having those requirements in mind let implement a solution.

Logging Tool

Firstly, we should choose a logging tool. There is a couple of options worth considering but based on my experience Serilog* is the way to go.

Why Serilog?

  • Easy to use
  • Flexible
  • Great documentation
  • Sinks addons (MsSql, Seq etc).

Code:

Let’s install Serilog NuGet packages in our ModularMonolith.Infrastructure project:

Next, let’s create a new extension method that will register a Logger for us.

The code above tries to resolve variables from appsettings.json(14–16) and then register a logger with corresponding “sinks”.

The configuration file looks like this:

  • EnableConsole and EnableFile will tell the logger to which sink log
  • FilePath — path to the file if EnableFile enabled. The path will contain our log file.
  • LogLevel — Tells logger which logs should be written to “sink”. Only the logs from chosen level and above will be logged.

In the WebApi project let’s add our new extension — this will enable logger in the application.

Let’s see what will happen now if we run the application and try to do a request to some endpoint:

Logger output in the console

As you can see on the above image all-important messages are listed in console output. The same output is saved to file on the server hard drive so we can back to it in case the application shutdown. We are able to add new sinks to our application if needed. Depends on what exactly do we want to achieve there are multiple Serilog sinks extensions.

You can find all available sinks here:
https://github.com/serilog/serilog/wiki/Provided-Sink

SEQ

Seq is the intelligent search, analysis, and alerting server built specifically for modern structured log data. It will allow us to view logs in a very convenient way.
Let’s see how easy we can add it to the ModularMonolith solution.

First, install an extra sink for Serilog:
https://github.com/serilog/serilog-sinks-seq
Then we will update our LoggerConfiguration class.

In line 32 we see a new section — all that we need to do is call the new WriteTo method.
As a parameter, we should pass a URL address of our Seq server and API Key.

How to set up a Seq server

  • By installing it on your computer/server.
  • By using Docker.

We will use the second approach in this article:

Firstly install docker on your machine:
https://www.docker.com/products/docker-desktop
Then all that you need to do is call this in your console:

Now when we go to http://localhost:5341/ we should see a seq page:

To make our logs appear on this page we have to do one more thing.
Navigate to: http://localhost:5341/#/settings/api-keys and create an API key that is used in our application to configure sink:

Summary

Logs provide information about the flow of the request, the time needed to finish it and all exceptions that occur in our application. Having these pieces of information we are able to troubleshoot problems much faster.

Using such tools as Seq makes working with logs much faster and efficient, especially when we are the support team :)

In the next article, we will take a look at global exception handling and how to log that information from any of our modules.

The whole code is available here:

https://github.com/Ridikk12/ModularMonolith/tree/Logging

Previous:

In Next Part:

  • Exception Handling — Observability

In future (this list can change):

  • Authentication/Authorization
  • OutBox improvements.
  • Domain events.
  • Unit/Integration tests.
  • Storing configuration.
  • Direct communication between modules.
  • The database approaches (multiple data sources).
  • Preparing for Microservices (Replacing MediatR by RabbitMq).
  • Migrate to Microservices.

References:

Worth checking:

https://github.com/serilog/serilog-settings-configuration — As an alternative to our custom code, the whole Serilog configuration can be done by appsettings.json file using this package.

--

--

Solution Architect | Tech Lead | .Net Developer who searches for the perfect balance between business values and code quality.