Логирование в C# приложениях: лучшие практики .NET разработки

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

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

Начать курс

Логирование в C# приложениях: лучшие практики

Логирование — это не просто запись сообщений в файл. В современных .NET-приложениях это критически важный инструмент для мониторинга, отладки и аудита. Грамотно настроенное логирование позволяет быстро находить причины сбоев, анализировать производительность и обеспечивать соответствие требованиям безопасности. В этой статье мы разберём лучшие практики логирования в C#, которые помогут вам построить надёжную систему сбора диагностической информации.

Выбор библиотеки логирования: Serilog vs NLog vs встроенный ILogger

В .NET существует несколько популярных решений. Рассмотрим их сильные и слабые стороны.

  • Microsoft.Extensions.Logging (ILogger) — встроенный абстрактный интерфейс. Подходит для простых проектов. Не поддерживает структурированное логирование «из коробки».
  • Serilog — лидер по популярности. Отличается простотой настройки, мощной поддержкой структурированных логов и обширным набором sinks (приёмников данных).
  • NLog — зрелый фреймворк с гибкой конфигурацией через XML или код. Отлично подходит для legacy-систем и сложных сценариев маршрутизации.

Для нового проекта рекомендуется Serilog. Он легко интегрируется с ILogger и предоставляет лаконичный API. Пример подключения:

// Установка: dotnet add package Serilog.AspNetCore
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(); // Замена стандартного логгера

Правильное использование уровней логирования

Не путайте все сообщения в один уровень. Используйте следующую градацию:

  • Trace (0) — максимально подробная информация для отладки. Включает детали выполнения каждой функции. Включайте только в dev-среде.
  • Debug (1) — полезные данные для разработчика: значения переменных, результат выполнения методов.
  • Information (2) — бизнес-события: «Пользователь вошёл в систему», «Заказ создан». Должны быть понятны не только программисту.
  • Warning (3) — аномалии, которые не прерывают работу: превышение времени выполнения, повторные попытки подключения.
  • Error (4) — исключения и сбои, требующие вмешательства. Всегда логируйте полный стек вызовов.
  • Critical (5) — фатальные ошибки: потеря соединения с БД, переполнение памяти. Требуют немедленного оповещения.

Пример правильного логирования в контроллере:

[ApiController]
[Route("api/orders")]
public class OrdersController : ControllerBase
{
    private readonly ILogger<OrdersController> _logger;

    public OrdersController(ILogger<OrdersController> logger)
    {
        _logger = logger;
    }

    [HttpPost]
    public IActionResult CreateOrder(OrderDto order)
    {
        _logger.LogInformation("Создание заказа {OrderId} для пользователя {UserId}", 
            order.Id, order.UserId);
        
        try
        {
            // бизнес-логика
            _logger.LogInformation("Заказ {OrderId} успешно создан", order.Id);
            return Ok();
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Ошибка при создании заказа {OrderId}", order.Id);
            return StatusCode(500);
        }
    }
}

Структурированное логирование и контекст

Избегайте конкатенации строк в логах. Вместо Log.Information("User: " + user.Name) используйте шаблоны с параметрами:

_logger.LogInformation("Пользователь {UserName} выполнил действие {Action}", 
    user.Name, "UpdateProfile");

Это позволяет поисковым системам (например, Elasticsearch) индексировать каждое поле отдельно. Вы сможете фильтровать логи по конкретному пользователю или типу действия.

Добавляйте контекстную информацию с помощью LogContext (Serilog) или Scope (NLog):

using (LogContext.PushProperty("RequestId", httpContext.TraceIdentifier))
using (LogContext.PushProperty("UserId", currentUser.Id))
{
    _logger.LogInformation("Обработка запроса");
    // Все логи внутри блока будут содержать RequestId и UserId
}

Ротация логов и централизованный сбор

Никогда не пишите все логи в один файл без ротации. Это приведёт к переполнению диска. Настройте ротацию по размеру или дате:

// Serilog: ротация каждый день, хранение 30 дней
.WriteTo.File("logs/app-.log", 
    rollingInterval: RollingInterval.Day,
    retainedFileCountLimit: 30)

Для production-среды используйте централизованные системы сбора:

  • Elastic Stack (ELK) — Elasticsearch + Logstash + Kibana. Serilog отправляет логи в Elasticsearch через sink Serilog.Sinks.ElasticSearch.
  • Seq — простой в настройке инструмент для просмотра структурированных логов.
  • Graylog — альтернатива ELK с веб-интерфейсом.

Пример отправки логов в Elasticsearch:

Log.Logger = new LoggerConfiguration()
    .WriteTo.Elasticsearch(new ElasticsearchSinkOptions(new Uri("http://localhost:9200"))
    {
        IndexFormat = "myapp-logs-{0:yyyy.MM.dd}",
        AutoRegisterTemplate = true
    })
    .CreateLogger();

Заключение

Логирование — это не просто техническая задача, а стратегический инструмент. Следуя этим практикам, вы получите:

  • Возможность быстро выявлять и устранять инциденты.
  • Понятную картину работы приложения для бизнес-аналитики.
  • Снижение затрат на поддержку за счёт автоматизации анализа.

Начните с малого: подключите Serilog, настройте структурированные логи и добавьте ротацию. Затем постепенно внедряйте централизованный сбор. Помните: хороший лог — это тот, который вы сможете прочитать через полгода и понять, что произошло.

Блоги

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