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, что удобно для дальнейшей обработки.
- Гибкость: множество готовых пакетов‑синков для отправки логов куда угодно.
Структура записи лога с 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 же делает отдельные поля, с которыми потом можно работать аналитически.
Гибкая настройка: уровни, фильтры, разные «синки»
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 | Краткое описание | Где используется |
|---|---|---|
| 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
{
// some code
}
catch (Exception ex)
{
Log.Error(ex, "Произошла ошибка при выполнении запроса");
}
Злоупотребление конфигом: не превращайте конфигурацию в свалку — добавляйте только нужные синки и уровни.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ