Ostatnio opisałem jak dodać logowanie przy pomocy biblioteki Serilog do naszej aplikacji (link). W tym poście postaram się pokazać jak można je skonfigurować.
Do tej pory konfiguracja naszego loggera była bardzo podstawowa:
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.WriteTo.File("logs.log")
.CreateLogger();
Teraz ją trochę rozbudujemy.
Zmiana poziomu logowania
W naszym przykładzie logujemy wszystko od poziomu „Information”. Poziomy logowania (wraz z ich hierarchią) prezentują się następująco:
- Verbose
- Debug
- Information
- Warning
- Error
- Fatal
Czyli w naszym przykładzie będziemy logować wszystko z poziomem: „Information”, „Warning”, „Error” oraz „Fatal” (więcej o poszczególnych poziomach logowania możemy poczytać tutaj).
Nadpisanie poziomu logowania
Gdy dodamy logowanie z użyciem Serilog, to logowane będą nie tylko nasze logi, ale również logi z przestrzeni nazw Microsoft oraz System. Jeśli nie chcemy logować wszystkiego, to możemy nadpisać poziom logowania dla tych miejsc. Można to zrobić w następujący sposób:
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.MinimumLevel.Override("System", LogEventLevel.Error)
.WriteTo.File("logs.log")
.CreateLogger();
Dzięki takiemu zabiegowi nasze logi nadal będą logowane od poziomu „Information”, natomiast logi od Microsoft będą logowane od poziomu „Warning”, a logi Systemowe od poziomu „Error”.
Logowanie dodatkowych informacji
Bardzo często możemy chcieć rozszerzyć logowane informacje o nowe elementy. Abyśmy mogli to zrobić obiekt LoggerConfiguration udostępnia nam właściwość Enrich, np.:
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.Enrich.WithMachineName()
.Enrich.FromLogContext()
.Enrich.WithCorrelationIdHeader()
.Enrich.WithExceptionDetails()
.Enrich.WithProperty("ApplicationName", typeof(Program).Assembly.GetName().Name)
.Enrich.WithProperty("Environment", Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"))
.WriteTo.File("logs.log")
.CreateLogger();
Dzięki temu wraz z zalogowaną informacją będziemy mieli w logach dodatkowo:
- nazwę komputera,
- kontekst logowanej informacji,
- identyfikator korelacji,
- szczegóły wyjątku,
- nazwa aplikacji,
- środowisko.
Tu wymieniłem elementy, które najczęściej wykorzystuję w swoich systemach (niekoniecznie wszystkie naraz). O tym jakie jeszcze dodatkowe informacje można zalogować, możemy znaleźć tutaj.
Logowanie do kilku miejsc
Czasami będziemy chcieli logować informację do wielu miejsc (np. przydaje się dodatkowe logowanie do konsoli). Gdy chcemy, aby nasze logi trafiły do wielu miejsc, wystarczy dodać wiele elementów WriteTo, np.:
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.WriteTo.Console()
.WriteTo.File(
"logs/log.log",
rollingInterval: RollingInterval.Day
)
.CreateLogger();
Taki kod sprawi, że nasz logi wylądują zarówno w konsoli, jak i w folderze „logs” w pliku „log<data>.log”.
Format logów
Jeśli chcemy, aby nasze logi miały trochę bardziej przystępny format do analizy (np. json), to możemy jako parametr metody Console lub File (z właściwości WriteTo) przekazać obiekt implementujący interfejs ITextFormatter, np. RenderedCompactJsonFormatter:
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.WriteTo.Console(new RenderedCompactJsonFormatter())
.WriteTo.File(
new RenderedCompactJsonFormatter(),
"logs/log.log",
rollingInterval: RollingInterval.Day
)
.CreateLogger();
Tak sformatowane logi będzie nam łatwiej przefiltrować np. w Kibanie.
Konfiguracja z pliku
Konfigurację naszego loggera nie trzeba pisać w kodzie. Można ją również podać w ustawieniach (np. w pliku appsettings.json).
Jeśli naszego loggera konfigurujemy w klasie Startup, to konfigurację mamy już wczytaną w konstruktorze. Jeśli jednak robimy to w klasie Program, to musimy taką konfigurację wczytać osobno. Można to zrobić np. tak:
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", false, true)
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", true)
.AddJsonFile("appsettings.local.json", true)
.Build();
Gdy mamy już obiekt konfiguracji, to należy go przekazać do metody Configuration w obiekcie LoggerConfiguration:
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.CreateLogger();
I to tyle. Teraz tylko naszą konfigurację musimy umieścić w pliku konfiguracyjnym.
Plik konfiguracyjny json (ze wszystkimi ustawieniami wymienionymi wyżej), prezentowałby się następująco:
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Error"
}
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"formatter": "Serilog.Formatting.Compact.RenderedCompactJsonFormatter, Serilog.Formatting.Compact"
}
},
{
"Name": "File",
"Args": {
"formatter": "Serilog.Formatting.Compact.RenderedCompactJsonFormatter, Serilog.Formatting.Compact",
"path": "logs/log.log",
"rollingInterval": "Day"
}
}
],
"Enrich": [ "WithMachineName", "FromLogContext", "WithCorrelationIdHeader", "WithExceptionDetails" ],
"Properties": {
"Application": "ProjectName",
"Environment": "Development"
}
}
Konfiguracja w pliku i kodzie
Nie musimy podawać konfiguracji tylko w kodzie albo tylko w pliku. Można to połączyć i niektóre ustawienia podać w konfiguracji, a pozostałe umieścić w kodzie, np.:
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.WriteTo.Console(new RenderedCompactJsonFormatter())
.WriteTo.File(
new RenderedCompactJsonFormatter(),
"logs/log.log",
rollingInterval: RollingInterval.Day
)
.CreateLogger();
Pingback: dotnetomaniak.pl
Warto wspomnieć, że aby mieć możliwość korzystania czy to z Sinks („WriteTo”), czy też z Enrichers („Enrich”), należy dodać odpowiednie paczki/zależności.
To prawda, cenna uwaga!
Pingback: C# Logowanie - DanLogowanie
Możliwość komentowania została wyłączona.