Почему ваше C# приложение кричит, а вы не слышите?
Представьте: вы запускаете новую версию микросервиса на C# в полночь. Всё работает идеально... первые 10 минут. А потом — бац! — пользователи жалуются на тормоза, а вы сидите перед лог-файлом размером 2 ГБ и пытаетесь найти иголку в стоге сена. Знакомо? Если нет — вы либо гений, либо никогда не занимались мониторингом по-настоящему.
В этой статье мы разберём, как превратить хаос логов в стройную систему. Вы узнаете, как настроить логирование C#, чтобы не сойти с ума при деплое, и как построить мониторинг приложений, который реально помогает, а не просто рисует красивые графики.
С чего начать: философия правильного логирования
Многие разработчики думают: «Поставлю Console.WriteLine — и норм». Спойлер: нет, не норм. Настоящее логирование в C# — это structured logging (структурированное логирование). Вместо строк вида:
Log.Info("User " + userId + " bought item " + itemId);
Вы пишете:
Log.Information("User {UserId} bought item {ItemId}", userId, itemId);
Разница колоссальная. Во втором случае вы можете фильтровать логи по UserId, строить дашборды и быстро находить проблемы. Это основа диагностики ошибок C# в production.
Выбираем библиотеку: Serilog vs NLog vs log4net
На рынке .NET есть три главных игрока. Давайте сравним их по критериям, важным для реального проекта:
- Serilog — фаворит современного .NET. Идеален для structured logging, легко настраивается, поддерживает sinks (вывод в файл, базу, Elasticsearch). Рекомендую для новых проектов.
- NLog — ветеран с гибкой конфигурацией через XML/JSON. Отлично подходит для легаси-систем, где уже используется.
- log4net — старичок, который лучше не трогать без крайней нужды. Поддержка хромает, structured logging — через костыли.
Для трассировки .NET в современных приложениях Serilog — выбор №1. Давайте настроим его за 5 минут.
Настройка Serilog в C#: пошаговый гайд
Установите пакеты через NuGet:
dotnet add package Serilog
dotnet add package Serilog.Sinks.Console
dotnet add package Serilog.Sinks.File
dotnet add package Serilog.Sinks.Seq
Теперь — минимальная конфигурация в Program.cs для ASP.NET Core:
using Serilog;
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.WriteTo.Console()
.WriteTo.File("logs/app-.log", rollingInterval: RollingInterval.Day)
.WriteTo.Seq("http://localhost:5341") // для продвинутого мониторинга
.CreateLogger();
try
{
Log.Information("Starting web application");
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseSerilog(); // интеграция с хостом
// ... остальная настройка
var app = builder.Build();
app.Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "Application terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}
Обратите внимание на rollingInterval — логи будут ротироваться каждый день, а не расти до бесконечности. Это основа асинхронного логирования в production.
Структурированные логи: как извлекать максимум пользы
Допустим, у нас есть метод обработки заказа. Без структурирования вы бы написали:
logger.LogInformation("Processing order {OrderId} for user {UserId}", order.Id, user.Id);
С Serilog это превращается в JSON-объект, который можно парсить. А если добавить контекст через ILogger<T>:
public class OrderService
{
private readonly ILogger<OrderService> _logger;
public OrderService(ILogger<OrderService> logger)
{
_logger = logger;
}
public async Task ProcessOrder(Order order)
{
using (_logger.BeginScope(new Dictionary<string, object>
{
["OrderId"] = order.Id,
["UserId"] = order.UserId
}))
{
_logger.LogInformation("Processing order");
// бизнес-логика
_logger.LogInformation("Order processed successfully");
}
}
}
Теперь все логи внутри scope автоматически содержат OrderId и UserId. Это невероятно упрощает мониторинг приложений при поиске конкретной транзакции.
Мониторинг в реальном времени: Application Insights и Grafana
Логи — это хорошо, но видеть их в реальном времени — ещё лучше. Для C# приложений есть два популярных пути:
- Azure Application Insights — облачное решение от Microsoft. Встраивается за 2 минуты, показывает метрики, трейсы, исключения. Идеально для Azure-проектов.
- Seq + Grafana — self-hosted вариант. Seq принимает structured logs, Grafana рисует дашборды. Подходит для on-premise.
Подключим Application Insights к ASP.NET Core:
dotnet add package Microsoft.ApplicationInsights.AspNetCore
В Program.cs:
builder.Services.AddApplicationInsightsTelemetry();
И добавьте строку подключения в appsettings.json:
{
"ApplicationInsights": {
"ConnectionString": "InstrumentationKey=ваш-ключ"
}
}
Готово! Теперь каждое исключение, каждый медленный запрос автоматически отправляются в портал Azure. Вы можете настроить алерты на падение производительности — это спасёт вас ночью.
Health Checks: чтобы мониторинг был полезным
Просто логировать мало — нужно проверять, что сервис жив. Добавьте Health Checks в ASP.NET Core:
builder.Services.AddHealthChecks()
.AddDbContextCheck<AppDbContext>()
.AddUrlGroup(new Uri("https://external-api.com"), "External API");
app.MapHealthChecks("/health");
Теперь ваш эндпоинт /health возвращает статус 200, если всё ОК, и 503, если проблемы. Подключите к этому Uptime Kuma или аналоги — и вы узнаете о падении раньше пользователей.
Лучшие практики: как не утонуть в логах
Опытные разработчики C# знают: количество логов — не равно качеству. Вот 5 правил, которые превратят ваш мониторинг C# приложений в искусство:
- Не логируйте в цикле. Если у вас 10 000 итераций — не пишите лог на каждую. Используйте счётчики или пишите summary после цикла.
- Используйте уровни логирования.
Verbose— для отладки,Information— для биз