CodeGym /Kursy /C# SELF /Logowanie: Microsoft.Exten...

Logowanie: Microsoft.Extensions.Logging

C# SELF
Poziom 64 , Lekcja 0
Dostępny

1. Wprowadzenie

W programowaniu logowanie — to nie tylko "wypisać coś na konsolę". Logowanie to twój czarny skrzynka, GPS-tracker i wskaźnik w jednym. Bez logów duży program staje się czarną skrzynką-zagadką: "dlaczego serwis się zawiesił?", "dlaczego użytkownik nie otrzymał maila?", "czy w ogóle ktoś uruchamiał ten moduł w ciągu ostatnich 3 miesięcy?" — na wszystkie te pytania często można odpowiedzieć tylko jeśli logi są przechowywane i dostępne.

Po co logowanie w praktyce?

Wyszukiwanie i diagnostyka błędów
Jeśli program się zwiesił — logi opowiedzą, gdzie i dlaczego. Jeśli nie zwiesił się, ale działa dziwnie — logi wskażą kroki, które do tego doprowadziły.

Monitoring stanu
Z logów można dowiedzieć się, czy system działa teraz, ile jest zapytań do serwera, czy pojawiają się błędy, kto i co robi.

Bezpieczeństwo
Logowanie prób nieautoryzowanego dostępu, podejrzanej aktywności, błędów uwierzytelniania.

Audit
Kto, co i kiedy zrobił. Jeśli zwolnił się zły admin i usunął dane, po logach można wszystko odtworzyć (albo przynajmniej zrozumieć, co dokładnie zrobił).

Wsparcie w development i operacjach
Logi to nie tylko dla programisty, ale też dla testera, operatora, admina. Za pół roku powiesz do siebie: 'Dzięki, że dodałeś logi!'

Od Console.WriteLine do produkcyjnego logowania

Pierwsza reakcja początkującego: "A co, po prostu Console.WriteLine nie wystarczy?". Dla małych programów edukacyjnych to faktycznie bywa ok. Ale kiedy aplikacja działa na serwerze, pracuje równolegle, albo korzysta z niej dziesiątki i setki użytkowników, konsola już nie pomoże. Trzeba:

  • Oddzielić ważne informacje od debugowych.
  • Wysyłać logi nie tylko na konsolę, ale też do plików, baz danych, scentralizowanych systemów.
  • Zmieniac poziom szczegółowości logów (minimum — tylko błędy, maksimum — wszystko).
  • Automatycznie uzupełniać wpisy datą, czasem i dodatkowymi informacjami.
  • Elastycznie konfigurować.
  • A przede wszystkim — nie zmieniać kodu, żeby przekierować logi w inne miejsce.

Właśnie wtedy zaczyna się era "prawdziwych" loggerów.

2. Podstawy nowoczesnego systemu logowania w .NET

W ekosystemie .NET istnieje standardowy, potężny i elastyczny framework do logowania — Microsoft.Extensions.Logging. To część całej "nowej fali" bibliotek .NET, które pojawiły się razem z ASP.NET Core, ale teraz używa się ich wszędzie: na serwerze, na desktopie, a nawet w aplikacjach mobilnych.

Co jest w nim dobrego?

  • Abstrakcja — nie wiąże cię z konkretną implementacją (logger może pisać do pliku, konsoli, do chmury, albo jednocześnie wszędzie).
  • Wsparcie poziomów logowania (Trace, Debug, Information, Warning, Error, Critical).
  • Integracja z DI-containerem i wsparcie dla nowoczesnych aplikacji .NET.
  • Bogaty ekosystem rozszerzeń: wsparcie strukturacji logów, zaawansowanych formatterów i integracji.

Podstawowe pojęcia i klasy

Poznajmy kluczowe obiekty, które będą potrzebne do pracy z Microsoft.Extensions.Logging:

Klasa/Interfejs Przeznaczenie
ILogger<T>
Interfejs do logowania, typowany względem klasy
ILogger
Ogólny interfejs loggera
ILoggerFactory
Fabryka tworzenia instancji loggerów
ILoggingBuilder
Pozwala konfigurować logowanie w aplikacji
LogLevel
Enum poziomów logów (Trace, Debug, Information, ...)

Poziomy logowania

Podział wiadomości według ważności pozwala filtrować "szum" i znaleźć to, co potrzebne:

Poziom (LogLevel) Do czego używać?
Trace
Najbardziej szczegółowe debugowanie, "szum", dane czasowe
Debug
Podstawowe informacje debugowe
Information
Kluczowe zdarzenia dla normalnej pracy
Warning
Ostrzeżenia o potencjalnych problemach, ale system działa dalej
Error
Błędy wymagające uwagi, ale aplikacja nadal działa
Critical
Krytyczne awarie zagrażające całemu systemowi

3. Praktyka: dodajemy logowanie do naszej aplikacji

Nie będziemy pisać abstrakcyjnego kodu — rozbudujemy nasze demo. Załóżmy, że mamy prostą klasę kalkulatora, którą rozszerzamy w trakcie kursu.

Przykład: podstawowy kalkulator

public class Calculator
{
    public int Add(int a, int b)
    {
        return a + b;
    }
    // Pozostałe metody...
}

Teraz wbudujemy w niego logowanie. Do tego potrzebujemy interfejsu ILogger<Calculator>, który będziemy otrzymywać z zewnątrz (np. przez Dependency Injection, DI).

using Microsoft.Extensions.Logging;

public class Calculator
{
    private readonly ILogger<Calculator> _logger;

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

    public int Add(int a, int b)
    {
        int result = a + b;
        _logger.LogInformation("Wykonano dodawanie: {A} + {B} = {Result}", a, b, result);
        return result;
    }
}

Ciekawostka:
Zamiast konkatenacji stringów loggerzy wspierają szablony i nazwane parametry ({A}, {B}, {Result}), co pozwala robić logi strukturalnymi i wygodnymi do automatycznej analizy i wyszukiwania.

4. Jak stworzyć i skonfigurować logger w aplikacji konsolowej

1. Dodajemy pakiety NuGet

W projekcie będziesz potrzebować:

  • Microsoft.Extensions.Logging
  • Microsoft.Extensions.Logging.Console (jeśli chcesz pisać na konsolę)
  • (opcjonalnie) inne providery, jeśli potrzebujesz

2. Konfigurujemy logger

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

// Tworzymy DI-container
var serviceProvider = new ServiceCollection()
    .AddLogging(builder => {
        builder.AddConsole(); // Wyjście na konsolę
        builder.SetMinimumLevel(LogLevel.Debug); // Minimalny poziom logów
    })
    .BuildServiceProvider();

// Pobieramy instancję loggera dla potrzebnego typu
var logger = serviceProvider.GetRequiredService<ILogger<Calculator>>();

var calculator = new Calculator(logger);
calculator.Add(5, 3); // W logach pojawi się wiadomość

Typowy błąd początkujących
"Dlaczego log nie pojawia się w konsoli?" — sprawdź, czy dodany jest odpowiedni provider (.AddConsole()) i czy poprawnie ustawiony jest minimalny poziom logowania (SetMinimumLevel). Jeśli poziom jest wyższy, wiadomości mogą być po prostu "odfiltrowane"!

5. Przydatne niuanse

Logujemy błędy i nietypowe sytuacje

Załóżmy, że chcemy zalogować dzielenie przez zero. Dodajmy odpowiednią metodę:

public int Divide(int a, int b)
{
    if (b == 0)
    {
        _logger.LogError("Próba dzielenia przez zero! a={A}", a);
        throw new DivideByZeroException();
    }
    int result = a / b;
    _logger.LogInformation("Wykonano dzielenie: {A} / {B} = {Result}", a, b, result);
    return result;
}

Po co to robić?
W realnej aplikacji, kiedy coś idzie nie tak, logi z poziomem Error zwykle przyciągają specjalną uwagę: są automatycznie wysyłane do adminów, podświetlane w monitoringu i używane do alertów/powiadomień.

Użycie kategorii i znaczników (scopes)

Logger w .NET wspiera tzw. scopes — to dodatkowe metadata, które automatycznie dodawane są do wszystkich logów w obrębie określonego bloku kodu. Na przykład, gdy obsługujesz request webowy lub sesję użytkownika, możesz dodać identyfikator do scope.

using (_logger.BeginScope("UserId: {UserId}", 42))
{
    _logger.LogInformation("Rozpoczęto przetwarzanie danych użytkownika");
    // ...
}

Wszystkie wiadomości w bloku otrzymają dodatkową etykietę UserId: 42, co później ułatwi znajdowanie logów po użytkownikach lub operacjach.

Przykład: poziomy logowania w praktyce

_logger.LogTrace("To jest Trace — prawie nikt tego nie zobaczy");
_logger.LogDebug("To jest Debug — dla deweloperów");
_logger.LogInformation("To jest Information — dla zdarzeń normalnej pracy");
_logger.LogWarning("To jest Warning — ostrzeżenie o możliwym problemie");
_logger.LogError("To jest Error — błąd wymagający uwagi");
_logger.LogCritical("To jest Critical — System płonie, potrzebny strażak!");

Jeśli ustawiłeś SetMinimumLevel(LogLevel.Information), zobaczysz tylko wiadomości poziomów Information, Warning, Error, Critical.

Tip:
Zostawiaj logi Trace i Debug do szczegółowej diagnostyki podczas developmentu, a na produkcji zwykle włączaj tylko Information i wyżej, żeby nie rozdęć rozmiaru logów i nie zgubić ważnych informacji w "szumie".

Schemat wizualny: architektura nowoczesnego logowania

graph TD
A[Kod aplikacji] --ILogger<YourClass>--> B[Microsoft.Extensions.Logging]
B --> C1[Console Provider]
B --> C2[File Provider]
B --> C3[Cloud/Database Provider]
C1 -.-> D1[Logi w konsoli]
C2 -.-> D2[Logi w pliku]
C3 -.-> D3[Logi w systemie monitoringu]

subgraph Providers
    C1
    C2
    C3
end

6. Dodatkowe możliwości i rozszerzenia

Strukturalne logowanie:
Wartości parametrów można przechowywać nie tylko w stringu, ale też jako klucz-wartość, co umożliwia wyszukiwanie i agregację po tych parametrach (np. przez Seq, ELK/ElasticSearch czy Application Insights).

Providery logowania:
Można dodać dziesiątki różnych providerów: plikowy, dla Windows EventLog, Azure itd.

Konfigurowanie logowania przez appsettings.json
W ASP.NET Core logi można elastycznie konfigurować z pliku konfiguracyjnego, bez rekompilacji aplikacji.

Przykład ustawienia minimalnego poziomu logowania przez config (appsettings.json):

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "MyApp.Calculator": "Debug",
      "Microsoft": "Warning"
    }
  }
}

7. Typowe błędy przy pracy z Microsoft.Extensions.Logging

Błąd nr 1: Nieprawidłowy wybór poziomu logowania.
Używanie LogInformation dla błędów zamiast LogError lub LogCritical utrudnia znajdowanie problemów na produkcji.

Błąd nr 2: Ignorowanie strukturalnego logowania.
Konkatenacja stringów zamiast szablonów z placeholderami ({Parameter}) odbiera korzyści strukturalnego logowania, takie jak wyszukiwanie po parametrach.

Błąd nr 3: Nieprawidłowa konfiguracja poziomów logowania.
Jeśli minimalny poziom logów jest ustawiony zbyt wysoko (np. Warning zamiast Debug), ważne wiadomości mogą zostać odfiltrowane.

Błąd nr 4: Nadmiarowe lub zbyt skąpe logowanie.
Zbyt szczegółowe logi (np. Trace na produkcji) tworzą "szum", a brak logów utrudnia diagnostykę.

2
Zadanie
C# SELF, poziom 64, lekcja 0
Niedostępne
Podstawowe logowanie z użyciem 'Microsoft.Extensions.Logging'
Podstawowe logowanie z użyciem 'Microsoft.Extensions.Logging'
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION