Introduction
Serilog is a structural logging library for Microsoft .Net and has become the preferred logging library for .Net applications.
The stack of features in Serilog that make it an appealing choice for developing apps are:
- Serilog's vast ecosystem consisting of hundreds of integrations that cover 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 Enrichment and the same can be achieved by using LogContext.
From the above line, the code is pretty straightforward and needs no explaination.
Out of the box logging configuration in appsettings.json is not necessary. Only the below configuration is required in appsettings.json
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:
- 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 interesting then kindly like and share it.