Мониторинг C# приложений: метрики и алерты на продвинутом уровне
В современной разработке на C# (.NET) просто "работающее" приложение перестало быть достаточным критерием качества. Продакшн-системы требуют постоянного наблюдения: нужно знать, сколько памяти потребляет сервис, как быстро обрабатываются запросы, не растёт ли очередь в брокере сообщений. Без системы мониторинга и алертов вы рискуете узнать о проблеме только от пользователей. В этой статье мы разберём продвинутые техники сбора метрик, их визуализации и настройки алертов для C# приложений, используя современные инструменты вроде Prometheus, Grafana и OpenTelemetry.
Зачем нужен продвинутый мониторинг?
Базовый мониторинг (CPU, RAM, диск) не даёт полной картины. Для C# приложений критичны специфические метрики: количество сборок мусора (GC), время блокировок потоков, размер пула подключений к БД. Алерты на эти метрики позволяют реагировать до того, как произойдёт отказ. Продвинутый подход подразумевает три уровня:
- Infrastructure level — стандартные метрики сервера.
- Application level — метрики бизнес-логики (задержки, ошибки, частота вызовов).
- Business level — метрики, связанные с доменом (количество заказов, конверсия).
Мы сосредоточимся на application уровне, который даёт наибольший эффект для C# разработчиков.
Сбор метрик с помощью OpenTelemetry и Prometheus
OpenTelemetry (OTel) — стандарт де-факто для инструментирования .NET приложений. Он позволяет собирать метрики, трейсы и логи единым API. Рассмотрим пример интеграции с Prometheus.
Установите NuGet-пакеты:
dotnet add package OpenTelemetry.Extensions.Hosting
dotnet add package OpenTelemetry.Exporter.Prometheus.AspNetCore
dotnet add package OpenTelemetry.Instrumentation.AspNetCore
Настройка в Program.cs:
using OpenTelemetry.Metrics;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenTelemetry()
.WithMetrics(metrics => metrics
.AddAspNetCoreInstrumentation()
.AddRuntimeInstrumentation()
.AddPrometheusExporter());
var app = builder.Build();
app.MapPrometheusScrapingEndpoint(); // endpoint для Prometheus
app.Run();
Теперь по адресу /metrics ваш сервис будет отдавать метрики в формате Prometheus. Среди них — количество HTTP-запросов, время их обработки, метрики GC и памяти.
Кастомные метрики
Для бизнес-метрик используйте Meter и Counter:
using System.Diagnostics.Metrics;
public class OrderMetrics
{
private readonly Counter<int> _ordersCreated;
public OrderMetrics(IMeterFactory meterFactory)
{
var meter = meterFactory.Create("MyApp.Orders");
_ordersCreated = meter.CreateCounter<int>("orders.created.count");
}
public void OrderCreated() => _ordersCreated.Add(1);
}
// Регистрация в DI:
builder.Services.AddSingleton<OrderMetrics>();
Эти метрики автоматически попадут в Prometheus и будут доступны для построения дашбордов в Grafana.
Настройка алертов: от простых правил к многоуровневой системе
Сами по себе метрики бесполезны без алертов. В Prometheus алерты настраиваются через Alertmanager. Рассмотрим продвинутый сценарий: алерт на аномальное время ответа API.
Правило в Prometheus (alert.rules.yml)
groups:
- name: csharp_app_alerts
rules:
- alert: HighRequestLatency
expr: |
histogram_quantile(0.95,
rate(http_server_duration_seconds_bucket[5m])
) > 2
for: 2m
labels:
severity: critical
annotations:
summary: "Высокая задержка запросов ({{ $value }}s)"
description: "P95 latency превышает 2 секунды в течение 2 минут"
Это правило сработает, если 95-й перцентиль времени ответа превышает 2 секунды. Важно использовать for: 2m — это предотвращает ложные срабатывания из-за кратковременных всплесков.
Продвинутые стратегии алертов
- Multi-window multi-burst: алерт срабатывает только при превышении порога в двух окнах разной длительности (например, 1 минута и 5 минут).
- Алерты на отсутствие данных: если метрика перестала приходить (например, сервис упал).
- Алерты на скорость изменения: резкий рост числа ошибок за короткое время.
Пример алерта на отсутствие данных:
- alert: ServiceDown
expr: absent(up{job="myapp"}) == 1
for: 1m
annotations:
summary: "Сервис myapp не отвечает"
Health Checks и продвинутая диагностика
Встроенные Health Checks в .NET — это не только проверка "жив/не жив". На продвинутом уровне их можно использовать для алертов на состояние зависимостей.
builder.Services.AddHealthChecks()
.AddDbContextCheck<AppDbContext>()
.AddRedis("localhost:6379")
.AddUrlGroup(new Uri("https://api.example.com/health"), "External API");
Далее настройте HealthCheckPublisher, который будет отправлять метрики в Prometheus:
public class HealthCheckMetricsPublisher : IHealthCheckPublisher
{
private readonly Counter<int> _unhealthyCounter;
public HealthCheckMetricsPublisher(IMeterFactory meterFactory)
{
var meter = meterFactory.Create("MyApp.Health");
_unhealthyCounter = meter.CreateCounter<int>("health.unhealthy.total");
}
public Task PublishAsync(HealthReport report, CancellationToken cancellationToken)
{
foreach (var entry in report.Entries)
{
if (entry.Value.Status != HealthStatus.Healthy)
_unhealthyCounter.Add(1, new KeyValuePair<string, object?>("check", entry.Key));
}
return Task.CompletedTask;
}
}
// Регистрация:
builder.Services.Configure<HealthCheckPublisherOptions>(opts => opts.Period = TimeSpan.FromSeconds(30));
builder.Services.AddSingleton<IHealthCheckPublisher, HealthCheckMetricsPublisher>();