Логирование в C#: лучшие практики для начинающих разработчиков

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: Почему логирование — основа стабильного приложения

Любой разработчик, особенно начинающий, рано или поздно сталкивается с ситуацией, когда приложение падает, а понять причину невозможно. Без логов вы слепы. Логирование — это не просто запись сообщений в файл, а мощный инструмент мониторинга, отладки и анализа поведения программы. В экосистеме 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" />

Так

Блоги

Рекомендаций по книгам