Логирование в 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, настройте структурированные логи и добавьте ротацию. Затем постепенно внедряйте централизованный сбор. Помните: хороший лог — это тот, который вы сможете прочитать через полгода и понять, что произошло.