1. Wprowadzenie
Jeśli zwykłe tekstowe logowanie to twój dziennik z notatkami "Ja dziś jadłem", to strukturalne logowanie zamienia każdy wpis w kartę z polami: {Data: ..., Zdarzenie: "Zjadł", Kalorie: 500, Danie: "Smażone mięso"}. To znaczy, że potem możesz nie tylko przeczytać dziennik, ale i zbudować wykres kalorii za miesiąc, przefiltrować zdarzenia po daniu i dowiedzieć się, kiedy jadłeś za późno.
Dlaczego zwykły tekst to za mało?
Z samym tekstem wszystko jest proste... do pewnego momentu. Spróbuj zebrać statystyki błędów w logach, wolumen sprzedaży w sklepie internetowym, ciąg akcji jednego użytkownika po jego ID (np. 42), jeśli wszystkie dane to ściany tekstu. Strukturalne logowanie pozwala podłączyć analizę, a nawet AI do logów. Z nim można szukać anomalii, budować dashboardy i reagować na problemy automatycznie.
Zalety
- Pozwala logować nie tylko wiadomości, ale też powiązane z nimi dane (pola/właściwości).
- Logi można analizować automatycznie: liczyć, filtrować, budować raporty.
- Używane są standaryzowane formaty, takie jak JSON, które łatwo parsować maszynowo.
Serilog: co to i dlaczego?
Serilog (oficjalna strona: serilog.net, dokumentacja: github.com/serilog/serilog/wiki) — popularna biblioteka do strukturalnego logowania w .NET. Świetnie integruje się z Microsoft.Extensions.Logging, obsługuje wysyłkę do dziesiątek systemów (plik, konsola, Seq, ElasticSearch, Grafana, Azure itd.), minimalnie wpływa na wydajność i jest prosta w konfiguracji.
Czym Serilog różni się od "zwykłego logowania"?
- Struktura: logi to obiekty z polami, po których można filtrować (np. wszystkie błędy użytkownika o ID 42).
- Formaty: potrafi zapisywać nie tylko tekst, ale i JSON, XML, co ułatwia dalsze przetwarzanie.
- Elastyczność: masa gotowych pakietów‑sinków do wysyłki logów gdzie chcesz.
Struktura wpisu loga w Serilog
Rzućmy okiem na strukturalny log, zanim napiszemy kod.
{
"Timestamp": "2024-06-22T10:23:45.123Z",
"Level": "Information",
"MessageTemplate": "Użytkownik {UserId} zalogował się do systemu",
"Properties": {
"UserId": 42,
"IpAddress": "127.0.0.1"
}
}
Tutaj nawet podstawowa analiza zrozumie: chodzi o użytkownika nr 42 i jego adres IP.
2. Instalacja i podstawowa konfiguracja Serilog w projekcie C#
Krok 1. Instalacja pakietów NuGet
W Rider/Visual Studio przez NuGet Package Manager zainstaluj:
- Serilog
- Serilog.Sinks.Console (wyjście do konsoli)
- Serilog.Extensions.Logging (do integracji z Microsoft.Extensions.Logging)
Przez wiersz poleceń:
dotnet add package Serilog
dotnet add package Serilog.Sinks.Console
Krok 2. Minimalna konfiguracja
Dodamy konfigurację w Program.cs i zapisemy pierwszy log.
using System;
using Serilog;
namespace MySuperApp
{
class Program
{
static void Main(string[] args)
{
// 1. Podstawowa konfiguracja Serilog: wyjście do konsoli
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.CreateLogger();
// 2. Przykład strukturalnego logowania
int userId = 42;
string ip = "127.0.0.1";
Log.Information("Użytkownik {UserId} zalogował się do systemu z IP {IpAddress}", userId, ip);
Log.CloseAndFlush();
}
}
}
W konsoli zobaczymy mniej więcej coś takiego:
[10:30:16 INF] Użytkownik 42 zalogował się do systemu z IP 127.0.0.1
Później łatwo skierować wyjście do pliku JSON, Seq lub innego systemu.
3. Formatowanie logów: Message Template
W Serilog zamiast konkatenacji stringów używa się składni szablonów:
Log.Information("Operacja {Operation} na pliku {FileName}", "usuwanie", "test.txt");
To nie tylko ładny zapis — to strukturalne logowanie: do wpisu trafią pola Operation i FileName, dostępne do filtrowania i agregacji.
Czym to różni się od string.Format?
string.Format("Operacja {0} na pliku {1}", operation, fileName) po prostu skleja string z placeholderami {0}, {1}. Serilog robi z tego osobne pola, z którymi później można pracować analitycznie.
Elastyczna konfiguracja: poziomy, filtry, różne "sinki"
Serilog może zapisywać logi jednocześnie w kilka miejsc.
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.WriteTo.File("log.txt", rollingInterval: RollingInterval.Day)
.CreateLogger();
Teraz logi zapisywane są zarówno do konsoli, jak i do pliku z rotacją dzienną.
4. Przykład
Załóżmy, że robimy aplikację konsolową‑"notes", pozwalającą tworzyć wpisy użytkownika. Dodajmy strukturalne logowanie akcji.
using System;
using Serilog;
namespace NotesApp
{
class Program
{
static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.WriteTo.Console()
.WriteTo.File("notes-log.json", rollingInterval: RollingInterval.Day,
formatter: new Serilog.Formatting.Json.JsonFormatter())
.CreateLogger();
Console.WriteLine("Wpisz nazwę użytkownika:");
string userName = Console.ReadLine();
Log.Information("Użytkownik {UserName} uruchomił aplikację NotesApp", userName);
while (true)
{
Console.WriteLine("Wpisz tekst notatki (lub wpisz 'wyjście'):");
string note = Console.ReadLine();
if (note == "wyjście")
{
Log.Information("Użytkownik {UserName} zakończył pracę", userName);
break;
}
Log.Information("Użytkownik {UserName} stworzył notatkę: {NoteText}", userName, note);
}
Log.CloseAndFlush();
}
}
}
Komentarz:
Log rejestruje, kto uruchamia program, co wpisuje i kiedy kończy pracę. W pliku notes-log.json każdy wpis to obiekt JSON, który łatwo analizować.
5. Przydatne niuanse
Najlepsze praktyki strukturalnego logowania
- Nie nadużywaj poziomów Debug/Trace w produkcji — używaj Information i Warning rozsądnie.
- Używaj nazwanych parametrów w szablonach zamiast konkatenacji stringów.
- Nigdy nie loguj danych wrażliwych (hasła, tokeny, klucze).
- Loguj ważne zdarzenia biznesowe, nie tylko błędy i wyjątki.
- Skonfiguruj rotację i czyszczenie logów, żeby nie zabrakło miejsca na dysku.
Wizualizacja i analiza: Seq, Kibana, Application Insights
Serilog wspiera mnóstwo sinków — punktów docelowych, do których wysyłane są logi.
| Sink | Krótki opis | Gdzie używane |
|---|---|---|
| Console | Prosto do konsoli | Development, testy |
| File | Do lokalnego lub sieciowego pliku | Małe projekty, dev |
| Seq | Interfejs webowy z filtrowaniem i dashboardami | Enterprise, analityka |
| ElasticSearch | Mocny system przechowywania i analizy | Duże firmy |
| Azure Application Insights | Monitorowanie w chmurze i telemetria | Serwisy obciążone w Azure |
Seq (datalust.co/seq) — bardzo popularne rozwiązanie do developmentu i użytku wewnętrznego: wygodne filtrowanie, wyszukiwanie po polach i szybkie wdrożenie.
Tabelki i wizualizacja
Poniżej krótka tabela tego, co można logować strukturalnie:
| Co logujemy | Jak wygląda w Serilog | Przykład wartości |
|---|---|---|
| ID użytkownika | |
123 |
| Akcja | |
"Usuwanie" |
| Błąd | |
"Moduł rejestracji" |
| Czas operacji | {Elapsed:0.000} sek | 1.234 |
| Nazwa pliku | |
"report.pdf" |
Ciekawe bajery i dodatkowe możliwości Serilog
- Enrichers: dodają do każdego logu właściwości (np. .Enrich.WithMachineName()).
- Powiązane logi: dodawaj RequestId do korelacji łańcucha zdarzeń.
- Konfiguracja przez appsettings.json: wygodne rozwiązanie dla produkcji.
{
"Serilog": {
"MinimumLevel": "Debug",
"WriteTo": [
{ "Name": "Console" },
{ "Name": "File", "Args": { "path": "log.txt" } }
]
}
}
Advanced sinks: można wysyłać logi do Slacka, Telegrama, e‑mailem (ale ostrożnie, żeby nie dostać tysiąca wiadomości na każdy błąd).
6. Część praktyczna: integracja z Microsoft.Extensions.Logging
W .NET często używa się standardowego interfejsu ILogger, żeby nie wiązać się z konkretną biblioteką. Serilog można podpiąć jako provider.
Krok 1. Instalacja pakietu
dotnet add package Serilog.Extensions.Logging
Krok 2. Konfiguracja
using Microsoft.Extensions.Logging;
using Serilog;
// ...
// Skonfiguruj Serilog jak zwykle:
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.CreateLogger();
// Teraz używamy Microsoft.Extensions.Logging
var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddSerilog();
});
ILogger<Program> logger = loggerFactory.CreateLogger<Program>();
logger.LogInformation("Testowy komunikat: {TestValue}", 123);
// Nie zapominaj zamknąć:
Log.CloseAndFlush();
Komentarz:
Teraz cały twój kod korzystający z ILogger<T> jest niezależny od providera — w razie potrzeby możesz przejść na NLog lub Log4Net.
7. Typowe błędy przy pracy z Serilog
Przeładowanie logów: jeśli zapisujesz wszystko bez selekcji, przydatnej informacji będzie trudno szukać.
Logowanie wyjątków jako tekst: używaj przeciążenia z wyjątkiem — wtedy w logu trafi struktura błędu.
try
{
// some code
}
catch (Exception ex)
{
Log.Error(ex, "Wystąpił błąd podczas wykonania zapytania");
}
Nadużywanie konfiguracji: nie zamieniaj konfiguracji w śmietnik — dodawaj tylko potrzebne sinki i poziomy.
GO TO FULL VERSION