1. Вступ
У програмуванні логування — це не просто «вивести щось у консоль». Логування — це ваш чорний ящик, GPS‑трекер та індикатор водночас. Без логів великий застосунок перетворюється на чорну скриньку‑загадку: «чому сервіс підвисає?», «чому користувач не отримав електронний лист?», «а взагалі хтось запускав цей модуль за останні 3 місяці?» — на всі ці запитання часто можна відповісти лише тоді, коли логи зберігаються й доступні.
Навіщо логування потрібне в реальному житті?
Пошук і діагностика помилок
Якщо застосунок аварійно завершився — логи підкажуть, де й чому. Якщо не завершився, але працює нестандартно — логи покажуть кроки, що до цього призвели.
Моніторинг стану
За логами можна дізнатися, чи працює система зараз, скільки запитів надходить на сервер, чи виникають помилки, хто й що робить.
Безпека
Логування спроб неавторизованого доступу, підозрілої активності, помилок аутентифікації.
Аудит
Хто, що й коли зробив. Якщо звільнився недобросовісний адміністратор і видалив дані, за логами можна відновити картину подій (або принаймні зрозуміти, що саме він зробив).
Підтримка в розробці та експлуатації
Логи потрібні не лише розробнику, а й тестувальнику, оператору, адміністратору. За пів року ви самі собі скажете: «Дякую, що додали логи!»
Від Console.WriteLine до промислового логування
Перша реакція новачка: «А що, просто Console.WriteLine не вистачає?». Для маленьких навчальних програм цього справді достатньо. Але коли застосунок запускають на сервері, він працює паралельно або ним користуються десятки чи сотні користувачів, консоль уже не допоможе. Потрібно:
- Розділяти важливу інформацію та налагоджувальну.
- Надсилати логи не лише в консоль, а й у файли, бази даних, централізовані системи.
- Змінювати рівень деталізації логів (мінімум — лише помилки, максимум — усе підряд).
- Автоматично доповнювати записи датою, часом, додатковою інформацією.
- Гнучко конфігурувати.
- А головне — не переписувати код, щоб перенаправити логи в інше місце.
Саме тут починається ера «справжніх» логерів.
2. Основи сучасної системи логування в .NET
В екосистемі .NET існує стандартний, потужний і гнучкий фреймворк для логування — Microsoft.Extensions.Logging. Це частина «нової хвилі» бібліотек .NET, що зʼявилися разом із ASP.NET Core, але тепер її використовують повсюдно: на сервері, на десктопі й навіть у мобільних застосунках.
Чим зручний цей фреймворк?
- Абстракція — не привʼязує вас до конкретної реалізації (логер може писати у файл, консоль, у хмару або одночасно — всюди).
- Підтримка рівнів логування (Trace, Debug, Information, Warning, Error, Critical).
- Інтеграція з DI‑контейнером і з усіма сучасними .NET‑застосунками.
- Розвинена екосистема розширень: підтримка структурованого логування, розширених форматерів і інтеграцій.
Основні поняття та класи
Познайоммося з ключовими об’єктами, які знадобляться для роботи з Microsoft.Extensions.Logging:
| Клас/інтерфейс | Призначення |
|---|---|
|
Інтерфейс для логування, типізований класом |
|
Узагальнений інтерфейс логера |
|
Фабрика для створення екземплярів логерів |
|
Дозволяє налаштовувати логування в застосунку |
|
Перерахування рівнів логів (Trace, Debug, Information, …) |
Рівні логування
Розділення повідомлень за рівнем важливості дає змогу фільтрувати «шум» і швидше знаходити потрібне:
| Рівень (LogLevel) | Для чого використовувати? |
|---|---|
|
Максимально докладна діагностика, «шум», тимчасові дані |
|
Основна налагоджувальна інформація |
|
Ключові подієві повідомлення під час нормальної роботи |
|
Попередження про потенційні проблеми, але система продовжує працювати |
|
Помилки, що потребують уваги, але застосунок продовжує працювати |
|
Критичні збої, що загрожують усій системі |
3. Практика: додаємо логування в наш застосунок
Не писатимемо абстрактний код — продовжмо розвивати наш демонстраційний застосунок. Нехай у нас вже є простий клас калькулятора, який ми розширюємо у міру проходження курсу.
Приклад: базовий калькулятор
public class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
// Інші методи...
}
Тепер вбудуємо в нього логування. Для цього нам потрібен інтерфейс ILogger<Calculator>, який ми отримуватимемо ззовні (наприклад, через 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("Виконано додавання: {A} + {B} = {Result}", a, b, result);
return result;
}
}
Корисний факт:
Замість конкатенації рядків логери підтримують шаблони та іменовані параметри ({A}, {B}, {Result}), що дає змогу робити логи структурованими та зручними для подальшої автоматичної обробки й пошуку.
4. Як створити і налаштувати логер у консольному застосунку
1. Додаємо пакети NuGet
У вашому проєкті знадобиться:
- Microsoft.Extensions.Logging
- Microsoft.Extensions.Logging.Console (якщо потрібно виводити в консоль)
- (за потреби) інші провайдери
2. Налаштовуємо логер
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
// Створюємо DI-контейнер
var serviceProvider = new ServiceCollection()
.AddLogging(builder => {
builder.AddConsole(); // Виведення в консоль
builder.SetMinimumLevel(LogLevel.Debug); // Мінімальний рівень логів
})
.BuildServiceProvider();
// Отримуємо екземпляр логера потрібного типу
var logger = serviceProvider.GetRequiredService<ILogger<Calculator>>();
var calculator = new Calculator(logger);
calculator.Add(5, 3); // У логи потрапить повідомлення
Типова помилка новачків
«Чому лог не зʼявляється у консолі?» — перевірте, чи додано потрібний провайдер (.AddConsole()) і чи правильно вказано мінімальний рівень логування (SetMinimumLevel). Якщо поріг вищий, потрібні повідомлення можуть бути відфільтровані.
5. Корисні нюанси
Логуємо помилки та нестандартні ситуації
Припустімо, ми хочемо залогувати ділення на нуль. Додамо відповідний метод:
public int Divide(int a, int b)
{
if (b == 0)
{
_logger.LogError("Спроба ділення на нуль! a={A}", a);
throw new DivideByZeroException();
}
int result = a / b;
_logger.LogInformation("Виконано ділення: {A} / {B} = {Result}", a, b, result);
return result;
}
Навіщо це потрібно?
У реальному застосунку, коли щось іде не так, логи з рівнем Error зазвичай привертають особливу увагу: їх автоматично відправляють адміністраторам, підсвічують у моніторингу та використовують для сповіщень (алертів).
Використання категорій і міток (scopes)
Логер у .NET підтримує так звані scopes — це додаткові метадані, які автоматично додаються до всіх логів упродовж певного блоку коду. Наприклад, якщо ви обробляєте веб-запит або користувацьку сесію, можна додати її ідентифікатор у scope.
using (_logger.BeginScope("UserId: {UserId}", 42))
{
_logger.LogInformation("Почалася обробка даних користувача");
// ...
}
Усі повідомлення всередині блоку отримають додаткову мітку UserId: 42, що потім допоможе шукати логи за користувачами або операціями.
Приклад: рівні логування в дії
_logger.LogTrace("Це Trace — майже ніхто не побачить");
_logger.LogDebug("Це Debug — для розробників");
_logger.LogInformation("Це Information — для подій звичайної роботи");
_logger.LogWarning("Це Warning — попередження про можливу проблему");
_logger.LogError("Це Error — помилка, що потребує уваги");
_logger.LogCritical("Це Critical — критична відмова системи, потрібне негайне втручання!");
Якщо ви налаштували SetMinimumLevel(LogLevel.Information), то побачите тільки повідомлення рівнів Information, Warning, Error, Critical.
Порада:
Залишайте логи Trace і Debug для детальної діагностики на етапі розробки, а у продуктивному середовищі зазвичай вмикають тільки Information і вище, щоб не роздувати обсяг логів і не губити важливе серед «шуму».
Візуальна схема: архітектура сучасного логування
graph TD
A[Код застосунку] --ILogger<YourClass>--> B[Microsoft.Extensions.Logging]
B --> C1[Console Provider]
B --> C2[File Provider]
B --> C3[Cloud/Database Provider]
C1 -.-> D1[Логи в консоль]
C2 -.-> D2[Логи у файл]
C3 -.-> D3[Логи в систему моніторингу]
subgraph Providers
C1
C2
C3
end
6. Додаткові можливості та розширення
Структуроване логування:
Значення параметрів можна зберігати не лише в рядку, а й у вигляді пар «ключ — значення», що робить можливими пошук і агрегацію за цими параметрами (наприклад, через Seq, ELK/Elasticsearch або Application Insights).
Провайдери логування:
Можна додати десятки різних провайдерів: файловий, для Windows EventLog, Azure тощо.
Конфігурування логування через appsettings.json
В ASP.NET Core логи гнучко конфігуруються через файл конфігурації, без перекомпіляції застосунку.
Приклад налаштування мінімального рівня логування через конфігурацію (appsettings.json):
{
"Logging": {
"LogLevel": {
"Default": "Information",
"MyApp.Calculator": "Debug",
"Microsoft": "Warning"
}
}
}
7. Типові помилки під час роботи з Microsoft.Extensions.Logging
Помилка №1: Неправильний вибір рівня логування.
Використання LogInformation для помилок замість LogError або LogCritical ускладнює пошук проблем у продуктивному середовищі.
Помилка №2: Ігнорування структурованого логування.
Конкатенація рядків замість шаблонів із плейсхолдерами ({Parameter}) позбавляє переваг структурованого логування, зокрема пошуку за параметрами.
Помилка №3: Неправильне налаштування рівнів логування.
Якщо мінімальний рівень логів встановлено вищим за потрібний (наприклад, Warning замість Debug), важливі повідомлення можуть бути відфільтровані.
Помилка №4: Надмірне або недостатнє логування.
Занадто детальні логи (наприклад, Trace у продуктивному середовищі) створюють «шум», а відсутність логів ускладнює діагностику.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ