Serilog in ASP.NET Core 3.1

Introduction

Serilog is a structural logging library for Microsoft .Net and has become preferred logging library for .Net applications.

The stack of features in Serilog that make it appealing choice for developing apps :

  • Serilog’s vast ecosystem comprising of hundreds of integrations that cover from Console, File, Message Queue, Seq, Elastic Search, Azure Event Hub etc
  • Simple Api and easy to extend
  • The capability of seamless switching the output format to either plain text or Json.

Coding

Let’s begin by creating ASP.NET Core WebApi project. Thereafter, install Serilog.AspNetCore nuget package

dotnet add package Serilog.AspNetCore

Here’s the program file generated by Visual Studio/code

 public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }

Console Logging

Amend the Program file to enable console logging

public static void Main(string[] args)
        {
            Log.Logger = new LoggerConfiguration()
                .Enrich.FromLogContext()
                .WriteTo.Console()
                .CreateLogger();
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
            .UseSerilog()
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }

Most logging frameworks provide some way of additional log values across all the log statements. This is useful while providing key information like user id/request id at every log statement.

Serilog implements this by what they call as Enrichment and the same can be achieved by using LogContext.

Enrich.FromLogContext()

From the above line, code is pretty straightforward and needs no explaination.

Out of the box logging configuration in appsettings.json is not necessary. Only below configuration is required in appsettings.json

{
  "AllowedHosts": "*"
}

Let’s modify WeatherForecastController class to start logging in the console.

[ApiController]
    [Route("[controller]")]
    public class WeatherForecastController : ControllerBase
    {
        private static readonly string[] Summaries = new[]
        {
            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
        };

        private readonly ILogger<WeatherForecastController> _logger;

        public WeatherForecastController(ILogger<WeatherForecastController> logger)
        {
            _logger = logger;
        }

        [HttpGet]
        public IEnumerable<WeatherForecast> Get()
        {
            _logger.LogInformation("called weather forecast");
            var rng = new Random();
            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = rng.Next(-20, 55),
                Summary = Summaries[rng.Next(Summaries.Length)]
            })
            .ToArray();
        }

After running the application, the output is as follows

[22:16:13 INF] Request starting HTTP/1.1 GET https://localhost:5001/weatherforecast  
08/30/2020 22:16:10Loaded 'Anonymously Hosted DynamicMethods Assembly'. 
[22:16:13 INF] Executing endpoint 'SerilogVerify.Controllers.WeatherForecastController.Get (SerilogVerify)'
[22:16:13 INF] Route matched with {action = "Get", controller = "WeatherForecast"}. Executing controller action with signature System.Collections.Generic.IEnumerable`1[SerilogVerify.WeatherForecast] Get() on controller SerilogVerify.Controllers.WeatherForecastController (SerilogVerify).
08/30/2020 22:16:10[22:16:13 INF] called weather forecast
08/30/2020 22:16:10[22:16:14 INF] Executing ObjectResult, writing value of type 'SerilogVerify.WeatherForecast[]'.
08/30/2020 22:16:10Loaded 
[22:16:14 INF] Executed action SerilogVerify.Controllers.WeatherForecastController.Get (SerilogVerify) in 75.9537ms
08/30/2020 22:16:10[22:16:14 INF] Executed endpoint 'SerilogVerify.Controllers.WeatherForecastController.Get (SerilogVerify)'

We can see the log message being called for the Weather Forecast as “called weather forecast”

We can easily change the output format to JSON

public static void Main(string[] args)
        {
            Log.Logger = new LoggerConfiguration()
                .Enrich.FromLogContext()
                .WriteTo.Console(new RenderedCompactJsonFormatter())
                .CreateLogger();
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
            .UseSerilog()
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }

File Logging

Install the Serilog.Sinks.File nuget package

dotnet add package Serilog.Sinks.File

To configure the sinks in C# code, call WriteTo.File() while logging configuration

public static void Main(string[] args)
        {
            Log.Logger = new LoggerConfiguration()
                .Enrich.FromLogContext()
                .WriteTo.Console(new RenderedCompactJsonFormatter())       .WriteTo.Debug(outputTemplate:DateTime.Now.ToString())          .WriteTo.File("log.txt",rollingInterval:RollingInterval.Day)
                .CreateLogger();
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
            .UseSerilog()
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }

The rolling interval appends the time period of the file name such as log20200830.txt

Seq

Seq has a splendid support for Serilog’s feature such as Complex event data, enrichment and structural logging.

you can use docker for Seq using the below command

docker pull datalust/seq
docker run --name seq -d --restart unless-stopped -e ACCEPT_EULA=Y -p 5341:80 datalust/seq:latest

You can see Seq is running on the port number 5341.

Install Serilog.Sinks.Seq nuget package

dotnet add package Serilog.Sinks.Seq

To configure the sinks in C# code, call WriteTo.Seq() while logging configuration

public static void Main(string[] args)
        {
            Log.Logger = new LoggerConfiguration()
                .Enrich.FromLogContext()
                .WriteTo.Console(new RenderedCompactJsonFormatter())
                .WriteTo.Debug(outputTemplate:DateTime.Now.ToString())
                .WriteTo.File("log.txt",rollingInterval:RollingInterval.Day)
                .WriteTo.Seq("http://localhost:5341/")
                .CreateLogger();
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
            .UseSerilog()
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }

Added additional HTTP Get method to validate error logging in WeatherForecastController

 [HttpGet(template:"/api/error/{id}")]
        public ActionResult<string> GetError(int id)
        {
            try
            {
                if (id <= 0)
                {
                    throw new Exception($"id cannot be less than or equal to 0:{id}");
                }

                return Ok($"id is:{ id}");

            }
            catch (Exception ex)
            {
                var sb = new StringBuilder();
                sb.AppendLine($"Error message:{ex.Message}");
                sb.AppendLine($"Error stack trace:{ex.StackTrace}");
                _logger.LogError(sb.ToString());
            }

            return BadRequest("bad request");
        }

Now run the application & navigate to /api/error/0 and open the Seq by running http://localhost:5341

I hope you like the article. In case, you find the article as interesting then kindly like and share it.

One thought on “Serilog in ASP.NET Core 3.1

  1. Pingback: Logging with ElasticSearch, Kibana, Serilog using ASP.NET Core Docker – Articles on latest .net technology

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s