1. Вступ
Якщо звичайне текстове логування — це ваш щоденник із нотатками "Я сьогодні поїв", то структуроване логування перетворює кожен запис на картку з полями: {Дата: ..., Подія: "Поїв", Калорії: 500, Страва: "Смажене мʼясо"}. Це означає, що ви зможете не лише перечитати щоденник, а й побудувати графік за калоріями за місяць, відфільтрувати події за стравою й дізнатися, коли ви їли надто пізно.
Чому простого тексту мало?
Із простим текстом усе просто… до певного моменту. Спробуйте зібрати статистику за помилками в логах, обсягах продажів в інтернет‑магазині, ланцюжок дій одного користувача за його ID (наприклад, 42), якщо всі дані — це суцільні полотна тексту. Структуроване логування дозволяє додати аналітику й навіть ШІ до логів. З ним можна шукати аномалії, будувати дашборди й реагувати на проблеми автоматично.
Переваги
- Дозволяє логувати не лише повідомлення, а й пов’язані з ними дані (поля/властивості).
- Логи можна аналізувати автоматично: рахувати, фільтрувати, будувати звіти.
- Використовуються стандартизовані формати, такі як JSON, які легко обробляють програми.
Serilog: що це і чому саме він?
Serilog (офіційний сайт: serilog.net, документація: github.com/serilog/serilog/wiki) — популярна бібліотека для структурованого логування в .NET. Вона чудово інтегрується з Microsoft.Extensions.Logging, підтримує виведення у десятки різних систем (файл, консоль, Seq, ElasticSearch, Grafana, Azure тощо), мінімально впливає на продуктивність і проста в налаштуванні.
Чим Serilog відрізняється від «просто логування»?
- Структура: логи — це об’єкти з полями, за якими можна фільтрувати (наприклад, усі помилки користувача з ID 42).
- Формати: уміє писати не лише текст, а й JSON, XML, що зручно для подальшої обробки.
- Гнучкість: чимало готових sinkʼів для надсилання логів куди завгодно.
Структура запису лога з Serilog
Погляньмо на структурований лог, перш ніж писати код.
{
"Timestamp": "2024-06-22T10:23:45.123Z",
"Level": "Information",
"MessageTemplate": "Користувач {UserId} увійшов у систему",
"Properties": {
"UserId": 42,
"IpAddress": "127.0.0.1"
}
}
Навіть простий аналіз покаже: йдеться про користувача № 42 та його IP‑адресу.
2. Встановлення та базове налаштування Serilog у C#‑проєкті
Крок 1. Встановлення NuGet‑пакетів
У Rider/Visual Studio через NuGet Package Manager встановіть:
- Serilog
- Serilog.Sinks.Console (виведення у консоль)
- Serilog.Extensions.Logging (для інтеграції з Microsoft.Extensions.Logging)
Через командний рядок:
dotnet add package Serilog
dotnet add package Serilog.Sinks.Console
Крок 2. Мінімальне налаштування
Додайте конфігурацію в Program.cs і створіть перший лог‑запис.
using System;
using Serilog;
namespace MySuperApp
{
class Program
{
static void Main(string[] args)
{
// 1. Базове налаштування Serilog: виведення у консоль
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.CreateLogger();
// 2. Приклад структурованого логування
int userId = 42;
string ip = "127.0.0.1";
Log.Information("Користувач {UserId} увійшов у систему з IP {IpAddress}", userId, ip);
Log.CloseAndFlush();
}
}
}
У консолі ви побачите приблизно таке:
[10:30:16 INF] Користувач 42 увійшов у систему з IP 127.0.0.1
Далі легко налаштувати виведення у JSON-файл, Seq або іншу систему.
3. Форматування логів: Message Template
У Serilog замість конкатенації рядків використовується синтаксис шаблонів:
Log.Information("Операція {Operation} над файлом {FileName}", "видалення", "test.txt");
Це не просто гарний запис — це структуроване логування: до запису потраплять поля Operation і FileName, доступні для фільтрації та агрегації.
Чим відрізняється від string.Format?
string.Format("Операція {0} над файлом {1}", operation, fileName) просто поєднує рядок із плейсхолдерами {0}, {1}. Serilog же створює окремі поля, з якими потім можна працювати аналітично.
Гнучке налаштування: рівні, фільтри, різні «sinkʼи»
Serilog може писати логи одночасно в кілька місць.
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.WriteTo.File("log.txt", rollingInterval: RollingInterval.Day)
.CreateLogger();
Тепер логи пишуться і в консоль, і у файл із щоденною ротацією.
4. Приклад
Припустімо, ви створюєте консольний застосунок‑«блокнот», що дозволяє створювати записи користувача. Додамо структуроване логування дій.
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("Введіть імʼя користувача:");
string userName = Console.ReadLine();
Log.Information("Користувач {UserName} запустив застосунок NotesApp", userName);
while (true)
{
Console.WriteLine("Введіть текст нотатки (або введіть «вихід»):");
string note = Console.ReadLine();
if (note == "вихід")
{
Log.Information("Користувач {UserName} завершив роботу", userName);
break;
}
Log.Information("Користувач {UserName} створив нотатку: {NoteText}", userName, note);
}
Log.CloseAndFlush();
}
}
}
Коментар:
Лог фіксує, хто запускає програму, що вводить і коли завершує роботу. У файлі notes-log.json кожен запис — це JSON-об’єкт, який легко аналізувати.
5. Корисні нюанси
Найкращі практики структурованого логування
- Не зловживайте рівнями Debug/Trace у продакшні — використовуйте Information і Warning з розумом.
- Використовуйте іменовані параметри у шаблонах замість конкатенації рядків.
- Ніколи не логуйте чутливі дані (паролі, токени, ключі).
- Логуйте важливі бізнес‑події, а не лише помилки й винятки.
- Налаштуйте ротацію й очищення логів, щоб на диску не закінчилося місце.
Візуалізація та аналіз: Seq, Kibana, Application Insights
Serilog підтримує багато sinkʼів — кінцевих точок, куди надсилаються логи.
| Sink | Короткий опис | Де використовується |
|---|---|---|
| Console | Прямо в консоль | Розробка, тести |
| File | У локальний або мережевий файл | Невеликі проєкти, dev |
| Seq | Веб‑інтерфейс із фільтрацією та дашбордами | Enterprise, аналітика |
| ElasticSearch | Потужна система зберігання та аналізу | Великі компанії |
| Azure Application Insights | Хмарний моніторинг і телеметрія | Сервіси, що працюють в Azure |
Seq (datalust.co/seq) — дуже популярне рішення для розробки й внутрішнього використання: зручна фільтрація, пошук по полях і швидке розгортання.
Таблиці та візуалізація
Нижче — коротка таблиця того, що можна логувати структуровано:
| Що логувати | Як виглядає в Serilog | Приклад значення |
|---|---|---|
| ID користувача | |
123 |
| Дія | |
"Видалення" |
| Помилка | |
"Модуль реєстрації" |
| Час операції | {Elapsed:0.000} с | 1,234 |
| Імʼя файлу | |
"report.pdf" |
Цікаві особливості та додаткові можливості Serilog
- Enrichers: додають до кожного лога властивості (наприклад, .Enrich.WithMachineName()).
- Пов’язані логи: додавайте RequestId для кореляції ланцюжка подій.
- Конфігурація через appsettings.json: зручно для продакшну.
{
"Serilog": {
"MinimumLevel": "Debug",
"WriteTo": [
{ "Name": "Console" },
{ "Name": "File", "Args": { "path": "log.txt" } }
]
}
}
Advanced sinks: можна надсилати логи в Slack, Telegram, електронною поштою (але обережно, щоб не отримати тисячу листів на кожну помилку).
6. Практична частина: інтеграція з Microsoft.Extensions.Logging
У .NET часто використовують стандартний інтерфейс ILogger, щоб не прив’язуватися до конкретної бібліотеки. Serilog можна під’єднати як провайдер.
Крок 1. Встановлення пакета
dotnet add package Serilog.Extensions.Logging
Крок 2. Конфігурування
using Microsoft.Extensions.Logging;
using Serilog;
// ...
// Налаштовуємо Serilog, як зазвичай:
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.CreateLogger();
// Тепер використовуємо Microsoft.Extensions.Logging
var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddSerilog();
});
ILogger<Program> logger = loggerFactory.CreateLogger<Program>();
logger.LogInformation("Тестове повідомлення: {TestValue}", 123);
// Не забудьте закрити:
Log.CloseAndFlush();
Коментар:
Тепер увесь ваш код на ILogger<T> незалежний від провайдера — за бажання можна перейти на NLog чи Log4Net.
7. Типові помилки під час роботи з Serilog
Перевантаження логів: якщо писати усе підряд, корисну інформацію буде складно знайти.
Логування винятків як тексту: використовуйте перевантаження з винятком — так у лог потрапить структура помилки.
try
{
// якийсь код
}
catch (Exception ex)
{
Log.Error(ex, "Сталася помилка під час виконання запиту");
}
Зловживання конфігурацією: не перетворюйте конфігурацію на звалище — додавайте лише потрібні sinkʼи та рівні.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ