Введение: Почему мониторинг C# приложений — это искусство
В современной разработке C# ( .NET) мониторинг перестал быть просто опцией. Это обязательный элемент production-среды, который позволяет не только фиксировать сбои, но и предсказывать их. На продвинутом уровне мониторинг превращается в систему раннего предупреждения: вы не просто смотрите на дашборды, а настраиваете алерты на основе сложных метрик.
В этой статье мы разберем, как выйти за рамки стандартных счетчиков CPU и памяти. Вы узнаете, как собирать кастомные бизнес-метрики, настраивать алерты с помощью Prometheus и Alertmanager, а также использовать OpenTelemetry для трассировки. Все примеры будут на C#.
1. Продвинутая инструментация: App Metrics и Prometheus
1.1. Подключение Prometheus к ASP.NET Core
Базовый мониторинг в .NET часто ограничивается встроенным PerformanceCounter. Однако для микросервисной архитектуры стандартом стал Prometheus. Установите пакет:
dotnet add package prometheus-net.AspNetCore
Затем добавьте middleware в Program.cs:
using Prometheus;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// Включаем эндпоинт метрик
app.UseHttpMetrics();
app.UseRouting();
app.MapMetrics(); // /metrics
app.Run();
Это даст вам базовые метрики: количество запросов, длительность, ошибки. Но для продвинутого уровня этого мало.
1.2. Кастомные бизнес-метрики
Допустим, вы хотите отслеживать количество созданных заказов или время выполнения критической бизнес-операции. Используйте Counter и Histogram:
using Prometheus;
public class OrderMetrics
{
private static readonly Counter OrdersCreated = Metrics
.CreateCounter("orders_created_total", "Total number of created orders");
private static readonly Histogram OrderProcessingTime = Metrics
.CreateHistogram("order_processing_seconds", "Histogram of order processing time");
public void RecordOrderCreation(double elapsedSeconds)
{
OrdersCreated.Inc();
OrderProcessingTime.Observe(elapsedSeconds);
}
}
Теперь в любом сервисе вы можете инжектировать OrderMetrics и записывать данные. Это позволит вам строить дашборды в Grafana с точностью до секунды.
2. Алерты: от простых триггеров к SLO-ориентированным правилам
2.1. Настройка Alertmanager для C# метрик
Собирать метрики — половина дела. Вторая половина — алерты. Создайте правило для Prometheus (alerts.yml):
groups:
- name: csharp_app_alerts
rules:
- alert: HighErrorRate
expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.05
for: 2m
labels:
severity: critical
annotations:
summary: "High error rate in C# app"
description: "Error rate is {{ $value }} for the last 5 minutes"
Это базовый пример. На продвинутом уровне стоит добавлять SLO-алерты. Например, если 99% запросов не укладываются в 200 мс — это повод для паники.
2.2. Алерты на основе бизнес-метрик
Используйте кастомные счетчики из предыдущего раздела. Если количество заказов резко упало — значит, что-то сломалось:
- alert: OrdersStopped
expr: rate(orders_created_total[10m]) == 0
for: 5m
labels:
severity: warning
annotations:
summary: "No orders created in last 10 minutes"
Такой алерт невозможно получить из стандартных метрик ОС — он требует глубокого понимания бизнес-логики.
3. OpenTelemetry: распределенная трассировка в C#
3.1. Интеграция OpenTelemetry SDK
Когда у вас 10 микросервисов на C#, метрики по отдельности не помогут. Нужна трассировка. Установите пакеты:
dotnet add package OpenTelemetry.Extensions.Hosting
dotnet add package OpenTelemetry.Exporter.Console
dotnet add package OpenTelemetry.Exporter.Jaeger
Настройка в Program.cs:
using OpenTelemetry.Trace;
builder.Services.AddOpenTelemetry()
.WithTracing(tracerProviderBuilder =>
tracerProviderBuilder
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddConsoleExporter()
.AddJaegerExporter(options =>
{
options.AgentHost = "localhost";
options.AgentPort = 6831;
}));
Теперь каждый HTTP-запрос будет иметь уникальный TraceId. Вы сможете увидеть, какой сервис тормозит.
3.2. Кастомные спаны и атрибуты
Для критических участков кода добавьте свои спаны. Например, при обработке заказа:
using System.Diagnostics;
public async Task ProcessOrder(Order order)
{
using var activity = Activity.Current?.Source
.StartActivity("ProcessOrder");
activity?.SetTag("order.id", order.Id);
activity?.SetTag("order.amount", order.Amount);
// Ваша бизнес-логика
await _paymentService.Charge(order);
activity?.SetStatus(ActivityStatusCode.Ok);
}
В Jaeger или Zipkin вы увидите не просто "запрос обработан", а полную картину: сколько времени заняла оплата, проверка склада и т.д.
4. Health Checks и продвинутая диагностика
4.1. Кастомные Health Checks
Встроенные health checks в ASP.NET Core — это хорошо. Но на продвинутом уровне вы должны проверять не только "жив ли сервис", но и его зависимости:
public class DatabaseHealthCheck : IHealthCheck
{
public async Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken = default)
{
try
{
using var connection = new SqlConnection(_connectionString);
await connection.OpenAsync(cancellationToken);
// Проверяем время ответа
var stopwatch = Stopwatch.StartNew();
// ... запрос
stopwatch.Stop();
if (stopwatch.ElapsedMilliseconds > 100)
return HealthCheckResult.Degraded("DB slow response");
return HealthCheckResult.Healthy();
}
catch (Exception ex)
{
return HealthCheckResult.Unhealthy("DB unreachable", ex);
}