JavaRush /Курсы /C# SELF /Мониторинг производительности и сбор метрик

Мониторинг производительности и сбор метрик

C# SELF
64 уровень , 3 лекция
Открыта

1. Введение

Давайте представим, что вы администратор сайта, и каждый день к вам приходит тысяча пользователей. В логах вы видите, что всё работает, ошибок почти нет (разве что кто-то иногда забывает пароль или ошибается с капчей). «Всё отлично!» — думаете вы.

И вдруг кто-то пишет в поддержку: сайт ужасно тормозит, страницы открываются по 10 секунд. Вы лезете в логи — ошибок нет! Ура? Нет, потому что логи рассказывают что случилось (или не случилось), но не говорят, насколько быстро или медленно это произошло, сколько ресурсов для этого понадобилось, и как менялось это поведение с ростом нагрузки.

Вот тут на сцену и выходят метрики — измеримые характеристики работы приложения. Это не только число ошибок, но и среднее время отклика, объём потребляемой памяти, количество запросов в секунду и другие показатели, по которым можно судить о здоровье системы.

Сравнение:
Логи — это "что случилось".
Метрики — это "насколько хорошо/плохо работает система".
Трассировка — это "почему система так работает (в деталях)".

Какие бывают метрики, и что стоит собирать?

Основные виды метрик:

Тип метрики Пример Для чего нужна
Счетчики (Counters) Число запросов, ошибок, сбоев Тренды, алерты, нагрузка
Гистограммы Время ответа, размер пакета Распределение значений, перцентили
Гейджи (Gauge) Использование памяти, CPU Текущее состояние ресурса
Сумматоры (Sum) Общий объём данных, байтов Общий объём операций за период

Примеры:

  • Среднее и 95-й перцентиль времени ответа на GET-запрос.
  • Количество пользователей онлайн прямо сейчас.
  • Использование памяти (Private Bytes, Working Set).
  • Частота возникновения ошибок типа 500/503.
  • Запросы к БД в минуту.

Эти показатели позволяют не только находить проблемы, но и предотвращать их — ведь повышенная нагрузка на сервер или «ползущий» рост времени ответа могут сигнализировать о будущих сбоях.

2. Как устроен сбор метрик в .NET и экосистеме OpenTelemetry

Общая архитектура

В современном .NET (начиная с .NET 6, особенно в .NET 8/9) существует стандартная система сбора метрик, основанная на OpenTelemetry.

Вот как это работает:

  1. Код приложения вызывает методы для увеличения счётчиков, регистрации гейджей, записи гистограмм.
  2. SDK OpenTelemetry Metrics собирает эти метрики (в памяти) и периодически отправляет их.
  3. Экпортёр метрик передаёт их в выбранную систему мониторинга (Prometheus, Application Insights, Grafana Cloud, Datadog и т.д.).
  4. Бэкенд мониторинга агрегирует, хранит, визуализирует, строит алерты и дашборды.
Схематичная блок-схема:

[Ваше приложение] 
       ⬇ 
 [Сбор метрик (OpenTelemetry SDK)]
       ⬇
 [Экспортёр метрик (Prometheus, AI, Datadog, ...)]
       ⬇
 [Система мониторинга/дашборды/алерты]

3. Практика: Основы работы с метриками в C#

Простые внутренние метрики: System.Diagnostics.Metrics

.NET предоставляет встроенный механизм для метрик — System.Diagnostics.Metrics.

Основные игроки: Meter, Counter<T>, Histogram<T>, ObservableGauge<T>.

Пример: счётчик посещений страницы


// Создаём Meter (обычно один на всё приложение)
using System.Diagnostics.Metrics;

static Meter meter = new Meter("MyCompany.MyApp", "1.0");

// Регистрируем счетчик
static Counter<long> homePageVisits = meter.CreateCounter<long>("HomePageVisits");

// Где-то в коде контроллера или сервиса...
public void HomePageRequested()
{
    homePageVisits.Add(1);
    // Дальше остальной код обработки страницы
}

Комментарии:

  • Meter — это «фабрика» для метрик, с уникальным именем (пространство имён приложения/компании).
  • CreateCounter<long> — создаёт счётчик; инкремент через Add(1).

Пример: измерение времени ответа


static Histogram<double> pageLoadTime = meter.CreateHistogram<double>("PageLoadTimeMs");

// В обработчике запроса:
public void OnRequest()
{
    var stopwatch = System.Diagnostics.Stopwatch.StartNew();

    // ...здесь сам запрос...

    stopwatch.Stop();
    pageLoadTime.Record(stopwatch.Elapsed.TotalMilliseconds);
}

Собираем гейджи для динамических значений

Гейдж — показатель, который меняется во времени: количество подключённых пользователей, текущий объём памяти и т.д.


static ObservableGauge<int> onlineUserGauge = meter.CreateObservableGauge(
    "OnlineUsers", 
    () => GetOnlineUserCount());

// Где GetOnlineUserCount - метод, возвращающий актуальное значение
static int GetOnlineUserCount()
{
    // Здесь должна быть ваша реальная логика!
    return ActiveUserList.Count;
}

В реальном мире всё это работает асинхронно: приложение сообщает значения метрик, а экспортёр забирает их и отдаёт наружу (например, Prometheus скрэпит endpoint "/metrics").

Добавление метрик в современное ASP.NET Core-приложение

Для ASP.NET Core многое доступно из коробки. Достаточно добавить пакет OpenTelemetry.Instrumentation.AspNetCore, и появятся метрики HTTP-запросов, времени ответа, числа ошибок и т.д.

Пример настройки в Program.cs:


using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOpenTelemetry()
    .WithMetrics(metrics =>
    {
        metrics
            .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("MyApp"))
            .AddAspNetCoreInstrumentation() // метрики HTTP
            .AddRuntimeInstrumentation()    // метрики среды выполнения .NET CLR
            .AddProcessInstrumentation()    // CPU/память процесса
            .AddMeter("MyCompany.MyApp")    // ваши метрики
            .AddPrometheusExporter();       // экспорт в Prometheus
    });

var app = builder.Build();

app.MapGet("/", () => "Hello World!");

app.Run();

Теперь ваше приложение будет выдавать метрики по адресу /metrics, которые можно скрэпить через Prometheus или другие системы.

4. Практические примеры использования метрик

Мониторинг производительности в реальных проектах

Подключаем метрики, чтобы узнать:

  • Какой средний и пиковый RPS (requests per second) выдерживает API?
  • Где «узкие места»: один эндпоинт работает 300 мс, другой — 2000 мс?
  • Сколько времени тратится на вызовы БД? (добавляем собственные гистограммы)

Пример: контролируем время ответа к базе данных


static Histogram<double> dbQueryDuration = meter.CreateHistogram<double>("DbQueryDurationMs");

public async Task<List<Product>> GetProductsAsync()
{
    var sw = Stopwatch.StartNew();
    var result = await _db.Products.ToListAsync();
    sw.Stop();
    dbQueryDuration.Record(sw.Elapsed.TotalMilliseconds);
    return result;
}

Пример: подсчитываем число ошибок


static Counter<long> apiErrors = meter.CreateCounter<long>("ApiErrors");

public IActionResult SomeEndpoint()
{
    try
    {
        // какое-то действие
        return Ok();
    }
    catch (Exception)
    {
        apiErrors.Add(1);
        throw;
    }
}

Работа с лейблами (тэгами) для метрик

Важно группировать данные по полезным признакам: эндпоинт, тип ошибки, тип пользователя и т.д.


homePageVisits.Add(
    1, 
    KeyValuePair.Create<string, object>("UserType", "Admin"));

Или для гистограммы:


dbQueryDuration.Record(
    sw.Elapsed.TotalMilliseconds, 
    KeyValuePair.Create<string, object>("QueryType", "GetProducts"));

Благодаря тегам в Grafana можно строить графики не только по всему приложению, но и по конкретным сегментам.

5. Интеграция с Prometheus, Application Insights, Datadog, Grafana

Экспортёры и интеграция

  • Prometheus — популярный open-source мониторинг, де-факто стандарт для облаков и Kubernetes.
  • Application Insights — облачная интеграция для Azure.
  • Datadog, Grafana Cloud — для профессиональных инфраструктур.

Все эти системы могут собирать метрики из .NET через экспортёры OpenTelemetry. Документация по экспортёрам OTel

Prometheus (шаги):

  1. Добавить NuGet пакет: OpenTelemetry.Exporter.Prometheus.
  2. Добавить .AddPrometheusExporter() в регистрацию метрик.
  3. В Grafana настроить datasource на Prometheus и построить дашборды.

Полезные ссылки:

6. Особенности, подводные камни и типичные ошибки

Один из частых промахов — чрезмерная детализация тегов. Если присваивать тегам слишком много уникальных значений (например, ID пользователя/заказа), число временных рядов взлетит — это приведёт к перегрузке хранилища метрик и росту затрат (так называемая cardinality explosion). Держите теги «грубозернистыми».

Разработчики иногда игнорируют System.Diagnostics.Metrics и готовые инструменты, делая «велосипед» через логи и таймеры. В итоге мониторинг хуже интегрируется и сложнее поддерживается. Используйте стандартные инструменты и автоматическую инструментализацию.

Ещё один промах — метрики собираются, но не экспортируются. Настройка экспортёра обязательна: добавьте, например, .AddPrometheusExporter() и убедитесь, что эндпоинт /metrics доступен для скрэпинга.

И, наконец, путаница между типами метрик: среднее время отклика считают через Counter, хотя нужно использовать Histogram — иначе вы не увидите пики и распределение. Счётчики — для количества; гистограммы — для времени/размеров/распределений; гейджи — для текущих состояний.

2
Задача
C# SELF, 64 уровень, 3 лекция
Недоступна
Измерение времени выполнения операции с помощью гистограммы
Измерение времени выполнения операции с помощью гистограммы
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ