🔥 Zapisy zamknięte, ale możesz pobrać Roadmapę .NET i dołączyć do listy oczekujących — Pobierz i dołącz do Listy VIP →

Logowanie w .NET — ILogger, Serilog i logowanie strukturalne

Console.WriteLine działa lokalnie, na Twoim komputerze. Na produkcji, gdzie aplikacja działa na serwerze, którego nie widzisz na żywo, jedynym sposobem, żeby dowiedzieć się co się dzieje (albo co się zepsuło), jest log — ale musi to być log, który da się później przeszukać i przefiltrować.

ILogger — wbudowany interfejs .NET

public class ZamowienieService
{
    private readonly ILogger<ZamowienieService> _logger;
    public ZamowienieService(ILogger<ZamowienieService> logger) => _logger = logger;

    public void ZlozZamowienie(int klientId, decimal suma)
    {
        _logger.LogInformation("Zamówienie złożone przez klienta {KlientId} na kwotę {Suma}", klientId, suma);
    }
}

ILogger<T> jest dostępny przez wstrzykiwanie zależności (Dependency Injection) w każdej klasie zarejestrowanej w kontenerze DI — parametr T (tutaj ZamowienieService) automatycznie oznacza każdy wpis, z której klasy pochodzi.

Poziomy logów — nie wszystko jest tak samo ważne

_logger.LogTrace("Wejście do metody ZlozZamowienie");       // szczegóły diagnostyczne, zwykle wyłączone
_logger.LogDebug("Sprawdzam stan magazynu");                 // przydatne podczas developmentu
_logger.LogInformation("Zamówienie {Id} złożone", zamowienie.Id); // normalne zdarzenia biznesowe
_logger.LogWarning("Stan magazynu poniżej progu dla {Sku}", sku); // coś wymaga uwagi, ale nie jest błędem
_logger.LogError(ex, "Nie udało się zapisać zamówienia {Id}", zamowienie.Id); // coś poszło nie tak
_logger.LogCritical("Baza danych niedostępna — aplikacja nie działa"); // system nie działa

Konfiguracja (appsettings.json) określa próg — np. w produkcji zwykle wyłącza się Trace/Debug, żeby nie zalewać logów szumem, a zostawia Information i wyżej.

Problem ze zwykłą interpolacją stringów

// ❌ tekst gotowy, ale bez struktury
_logger.LogInformation($"Zamówienie {zamowienie.Id} złożone przez klienta {klientId}");

Ten log wygląda dobrze na ekranie, ale w systemie agregującym logi (Seq, ELK, Application Insights) to tylko jeden długi string. Żeby znaleźć wszystkie zamówienia konkretnego klienta, musiałbyś przeszukiwać tekst — zawodne i wolne.

Logowanie strukturalne — te same dane, ale przeszukiwalne

// ✅ named placeholders zamiast interpolacji
_logger.LogInformation("Zamówienie {ZamowienieId} złożone przez klienta {KlientId}", zamowienie.Id, klientId);

Kluczowa różnica: {ZamowienieId} i {KlientId} to nazwane placeholdery (message template), nie interpolacja stringów. Logger zapisuje zarówno gotowy tekst, jak i osobno każdą wartość jako pole danych. W systemie takim jak Seq możesz teraz zapytać dokładnie: “pokaż wszystkie logi, gdzie KlientId = 42” — bez przeszukiwania tekstu, bo KlientId to realne, ustrukturyzowane pole, nie fragment zdania.

Serilog — alternatywa z realną konfiguracją sinków

Wbudowany ILogger działa, ale konfiguracja miejsc docelowych (plik, konsola, zewnętrzny serwis) jest ograniczona. Serilog to najpopularniejsza biblioteka logowania w .NET, z tym samym API ILogger, ale znacznie bogatszą konfiguracją:

dotnet add package Serilog.AspNetCore
dotnet add package Serilog.Sinks.File
// Program.cs
using Serilog;

Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Information()
    .WriteTo.Console()
    .WriteTo.File("logs/log-.txt", rollingInterval: RollingInterval.Day)
    .Enrich.FromLogContext()
    .CreateLogger();

builder.Host.UseSerilog();

WriteTo.Console() i WriteTo.File(...) to sinki (sinks) — miejsca, gdzie logi faktycznie trafiają. Możesz mieć ich wiele naraz: konsola do developmentu, plik z rotacją dzienną (RollingInterval.Day) do produkcji, a dodatkowo sink do Seq czy Application Insights. Enrich.FromLogContext() automatycznie dokłada do każdego logu dodatkowy kontekst (np. identyfikator żądania HTTP), bez ręcznego dopisywania go w każdym wywołaniu LogInformation.

Podsumowanie

Logowanie produkcyjne różni się od Console.WriteLine w dwóch wymiarach: poziomy logów (Trace przez Critical) pozwalają filtrować szum od realnych problemów, a logowanie strukturalne (named placeholders zamiast interpolacji stringów) zamienia log z linijki tekstu w przeszukiwalne dane. ILogger<T> to wbudowany, wystarczający punkt startowy — Serilog dokłada do tego samego API bogatszą konfigurację sinków (konsola, plik, zewnętrzne serwisy) i automatyczne wzbogacanie logów o kontekst.

👨‍💻
Mariusz Jurczenko
Senior .NET Developer · 10+ lat doświadczenia komercyjnego

Programista .NET z doświadczeniem komercyjnym w firmach takich jak NFZ, Kamsoft, Diagnostyka, Hermes Reply Polska czy Etisoft Smart Solutions. Twórca kursów, z których skorzystało już ponad 11 000 osób w Strefie Kursów i ponad 1 000 kursantów na dev-hobby.pl.

Specjalizacja: Clean Code, Clean Architecture i uczenie programowania tak, żeby dało się je naprawdę zrozumieć — nie wykuć.

🚀 Co dalej?

Zobacz to w praktyce na wideo i pobierz darmową roadmapę, żeby ułożyć naukę w spójną ścieżkę do pierwszej pracy.

Dodaj komentarz

czytanie to początek

Zamień wiedzę w umiejętności

Pobierz darmową Roadmapę .NET i ułóż takie tematy jak ten w spójną ścieżkę do pierwszej pracy.

Pobieram roadmapę →