Введение: Почему мониторинг критичен для C# приложений
В современной разработке программного обеспечения написать код — это лишь половина дела. Чтобы приложение работало стабильно, быстро и предсказуемо, необходим постоянный мониторинг. Для C# (.NET) приложений, которые часто используются в высоконагруженных корпоративных системах, банковском секторе, e-commerce и облачных сервисах, мониторинг становится не просто опцией, а обязательным требованием.
Без мониторинга вы работаете вслепую: утечки памяти остаются незамеченными до падения сервера, медленные запросы накапливаются, а пользователи уходят к конкурентам. В этой статье мы разберем полный цикл настройки наблюдаемости (observability) для C# приложений: от сбора базовых метрик до настройки умных алертов в реальном времени.
1. Основы мониторинга: какие метрики собирать в .NET
Прежде чем настраивать инструменты, важно понять, какие метрики действительно важны для C# приложений. Мы разделим их на три группы:
1.1. Системные метрики (уровень инфраструктуры)
- CPU и Memory — загрузка процессора и потребление оперативной памяти процессом dotnet.
- GC (Garbage Collection) — частота и длительность сборок мусора, размер куч (Gen0, Gen1, Gen2, LOH).
- Thread Pool — количество активных, свободных и ожидающих потоков.
- Disk I/O и Network — чтение/запись на диск, сетевые подключения.
1.2. Бизнес-метрики (уровень приложения)
- Request Rate — количество запросов в секунду (RPS).
- Latency — время ответа (p50, p95, p99 перцентили).
- Error Rate — процент 4xx и 5xx ошибок.
- Active Users — количество одновременно работающих пользователей.
1.3. Метрики .NET Runtime
- JIT Compilation — время компиляции методов.
- Assembly Loading — количество загруженных сборок.
- Exceptions — количество выброшенных исключений в секунду.
Для сбора этих метрик в .NET используется мощный инструмент — System.Diagnostics.Metrics (встроенный API с .NET 6+) и библиотека OpenTelemetry.
2. Настройка сбора метрик с помощью OpenTelemetry и Prometheus
Лучший способ организовать мониторинг C# приложения — использовать связку OpenTelemetry (сбор метрик) + Prometheus (хранение) + Grafana (визуализация). Рассмотрим пошаговую настройку.
2.1. Установка пакетов NuGet
Добавьте в проект следующие пакеты:
dotnet add package OpenTelemetry.Extensions.Hosting
dotnet add package OpenTelemetry.Exporter.Prometheus.AspNetCore
dotnet add package OpenTelemetry.Instrumentation.AspNetCore
dotnet add package OpenTelemetry.Instrumentation.Runtime
2.2. Конфигурация в Program.cs
Настройте OpenTelemetry для автоматического сбора метрик ASP.NET Core и .NET Runtime:
using OpenTelemetry.Metrics;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenTelemetry()
.WithMetrics(metrics => metrics
.AddAspNetCoreInstrumentation() // метрики HTTP запросов
.AddRuntimeInstrumentation() // метрики GC, JIT, ThreadPool
.AddPrometheusExporter()); // экспорт в Prometheus
var app = builder.Build();
app.UseOpenTelemetryPrometheusScrapingEndpoint(); // эндпоинт /metrics
app.Run();
Теперь по адресу http://localhost:5000/metrics будет доступен эндпоинт с метриками в формате Prometheus.
2.3. Создание пользовательских метрик
Добавим бизнес-метрику — счетчик обработанных заказов:
public class OrderMetrics
{
private readonly Counter<int> _ordersProcessed;
public OrderMetrics(IMeterFactory meterFactory)
{
var meter = meterFactory.Create("MyApp.Orders");
_ordersProcessed = meter.CreateCounter<int>("orders.processed");
}
public void OrderProcessed(string status)
{
_ordersProcessed.Add(1, new KeyValuePair<string, object?>("status", status));
}
}
// В контроллере:
public class OrderController : ControllerBase
{
private readonly OrderMetrics _metrics;
[HttpPost]
public IActionResult CreateOrder()
{
// ... бизнес-логика
_metrics.OrderProcessed("success");
return Ok();
}
}
3. Настройка алертов: от простых правил до умных порогов
Метрики бесполезны без алертов. Рассмотрим настройку оповещений на примере Prometheus Alertmanager и встроенных Health Checks в .NET.
3.1. Базовые алерты в Prometheus
Пример правил для файла alerts.yml:
groups:
- name: dotnet_alerts
rules:
- alert: HighMemoryUsage
expr: process_runtime_dotnet_memory_working_set > 500 * 1024 * 1024
for: 5m
labels:
severity: warning
annotations:
summary: "High memory usage ({{ $value }} MB)"
- alert: HighErrorRate
expr: rate(http_server_duration_seconds_count{status_code=~"5.."}[5m]) > 0.05
for: 2m
labels:
severity: critical
annotations:
summary: "Error rate above 5%"
- alert: GarbageCollectionPause
expr: rate(dotnet_gc_pause_time_ticks[1m]) > 1000000
for: 1m
labels:
severity: warning
annotations:
summary: "GC pause time too high"
3.2. Health Checks в .NET
Встроенный механизм Health Checks позволяет проверять зависимости приложения (БД, кэш, внешние API):
// Program.cs
builder.Services.AddHealthChecks()
.AddDbContextCheck<AppDbContext>()
.AddUrlGroup(new Uri("https://api.example.com"), "External API");
app