Введение: Почему логирование — основа стабильного приложения
Любой разработчик, особенно начинающий, рано или поздно сталкивается с ситуацией, когда приложение падает, а понять причину невозможно. Без логов вы слепы. Логирование — это не просто запись сообщений в файл, а мощный инструмент мониторинга, отладки и анализа поведения программы. В экосистеме C# (.NET) существует несколько зрелых фреймворков, которые позволяют гибко настраивать вывод логов. В этой статье мы разберём лучшие практики логирования для начинающих: от выбора библиотеки до структурированного вывода данных.
1. Выбор библиотеки для логирования: NLog vs Serilog
Для .NET-приложений существует два основных игрока: NLog и Serilog. Оба являются бесплатными, активно поддерживаются и имеют тысячи интеграций. Какой выбрать?
NLog
- Простота настройки через XML или код.
- Отличная производительность на высоких нагрузках.
- Встроенная поддержка ротации файлов, отправки email, записи в базу данных.
- Подходит для проектов, где нужен быстрый старт без лишних зависимостей.
Serilog
- Фокус на структурированное логирование (structured logging).
- Логи хранятся не как строки, а как объекты с полями — это упрощает поиск и анализ.
- Идеально для микросервисов и систем, где логи собираются в Elasticsearch или Grafana.
- Синтаксис с плейсхолдерами
{Property}делает код более читаемым.
Совет для начинающих: Начните с Serilog, если планируете работать с современными инструментами мониторинга. Если проект маленький и локальный — подойдёт NLog.
// Пример настройки Serilog в Program.cs (.NET 6+)
using Serilog;
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.WriteTo.Console()
.WriteTo.File("logs/app-.log", rollingInterval: RollingInterval.Day)
.CreateLogger();
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseSerilog(); // Подключаем Serilog к хосту
2. Уровни логирования: когда и что писать
Одна из самых частых ошибок новичков — писать всё подряд на уровне Info или Error. Это засоряет логи и делает их бесполезными. В .NET приняты следующие уровни (по возрастанию важности):
- Trace — мельчайшие детали: вход/выход из методов, итерации циклов. Используется только при глубокой отладке.
- Debug — отладочная информация для разработчика: значения переменных, состояние объектов.
- Information — ключевые события работы приложения: запуск, остановка, успешная обработка запроса.
- Warning — потенциальные проблемы, которые не ломают работу: медленный запрос, устаревшая версия API.
- Error — исключения и сбои, которые требуют вмешательства: ошибка БД, тайм-аут.
- Fatal — критические ошибки, после которых приложение не может продолжать работу.
// Пример использования разных уровней
ILogger<MyService> _logger;
public async Task ProcessOrderAsync(int orderId)
{
_logger.LogInformation("Начало обработки заказа {OrderId}", orderId);
try
{
var result = await _repository.GetOrderAsync(orderId);
if (result == null)
{
_logger.LogWarning("Заказ {OrderId} не найден", orderId);
return;
}
// ... логика
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка при обработке заказа {OrderId}", orderId);
throw; // Пробрасываем выше, если нельзя восстановиться
}
}
Лучшая практика: Не используйте Log.Error для обычных бизнес-ошибок (например, пользователь ввёл неверный пароль). Это Warning. Error — только для непредвиденных исключений.
3. Структурированное логирование: почему это важно
Традиционное логирование выглядит так:
_logger.LogInformation($"User {userId} made purchase of {amount} rubles");
Проблема: если вы захотите найти все логи по конкретному userId, вам придётся парсить строку. В Serilog и NLog 5.0+ вы пишете иначе:
_logger.LogInformation("User {UserId} made purchase of {Amount} rubles", userId, amount);
Теперь UserId и Amount — это именованные поля. В системе сбора логов (например, Elasticsearch или Seq) вы сможете делать запросы: UserId = 123 и получать точные результаты. Это называется structured logging.
Преимущества структурированного подхода:
- Автоматическая индексация полей в системах анализа.
- Возможность строить графики и дашборды.
- Безопасность: вы не рискуете случайно вывести в лог конфиденциальные данные, если используете корректные плейсхолдеры.
Совет: Никогда не используйте интерполяцию строк ($"...") в логах. Всегда передавайте параметры отдельно — это сохраняет структуру и улучшает производительность (логи могут не записываться, если уровень не достигнут).
4. Конфигурация и ротация логов
Если ваше приложение работает 24/7, логи будут расти бесконечно. Без ротации файлов вы рискуете заполнить весь диск. Настройка ротации — обязательный шаг.
Пример для Serilog (ротация по дням):
.WriteTo.File("logs/app-.log",
rollingInterval: RollingInterval.Day,
retainedFileCountLimit: 30) // хранить 30 дней
Пример для NLog (ротация по размеру):
<target name="file" xsi:type="File"
fileName="${basedir}/logs/app.log"
archiveFileName="${basedir}/logs/app.{##}.log"
archiveEvery="Day"
maxArchiveFiles="7"
archiveNumbering="Rolling" />
Так