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?
  • Де «вузькі місця»: один endpoint працює 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;
    }
}

Працюємо з тегами для метрик

Важливо групувати дані за корисними ознаками: endpoint, тип помилки, тип користувача тощо.


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() і переконайтеся, що endpoint /metrics доступний для скрейпінгу.

І нарешті, плутанина між типами метрик: середній час відгуку рахують через Counter, хоча треба використовувати Histogram — інакше ви не побачите піки та розподіл. Лічильники — для кількості; гістограми — для часу/розмірів/розподілів; гейджі — для поточних станів.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ