Мониторинг C++ приложений: метрики и алерты продвинутого уровня

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Мониторинг C++ приложений: продвинутые метрики и настройка алертов

В условиях высоконагруженных систем мониторинг C++ приложений перестаёт быть опцией — это необходимость. Базовый сбор CPU и памяти уже не даёт полной картины. На продвинутом уровне требуется отслеживать внутренние очереди, время выполнения критических секций, количество утечек ресурсов и даже состояние пулов потоков. В этой статье мы рассмотрим, как организовать сбор метрик, настроить алерты и интегрировать C++ приложение с современными системами мониторинга, такими как Prometheus и Grafana.

1. Сбор метрик на уровне приложения

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

Рассмотрим пример с использованием библиотеки Prometheus-CPP. Допустим, у нас есть обработчик HTTP-запросов, и мы хотим отслеживать:

  • Количество обработанных запросов (счётчик).
  • Время обработки запроса (гистограмма).
  • Количество ошибок (счётчик с метками).
#include <prometheus/counter.h>
#include <prometheus/histogram.h>
#include <prometheus/registry.h>

using namespace prometheus;

// Глобальный реестр метрик
std::shared_ptr<Registry> registry = std::make_shared<Registry>();

// Создаём счётчик запросов
auto& request_counter = BuildCounter()
    .Name("http_requests_total")
    .Help("Total number of HTTP requests")
    .Register(*registry);

// Создаём гистограмму для времени ответа
auto& request_duration = BuildHistogram()
    .Name("http_request_duration_seconds")
    .Help("Request duration in seconds")
    .Register(*registry);

// Функция обработки запроса
void handle_request(const std::string& endpoint) {
    auto start = std::chrono::high_resolution_clock::now();

    // Имитация обработки
    std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 100));

    auto end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> elapsed = end - start;

    // Обновляем метрики
    request_counter.Increment();
    request_duration.Observe(elapsed.count());
}

Такой подход позволяет получать точные данные о производительности каждого эндпоинта, а не усреднённые показатели системы.

2. Продвинутые метрики: очереди, пулы потоков и утечки

Для высоконагруженных C++ приложений критично отслеживать состояние внутренних ресурсов. Рассмотрим три ключевых типа метрик:

2.1. Глубина очередей сообщений

Если приложение использует очереди задач (например, на базе std::queue с мьютексами), добавьте датчик текущего размера очереди:

#include <prometheus/gauge.h>

auto& queue_size = BuildGauge()
    .Name("task_queue_size")
    .Help("Current number of tasks in queue")
    .Register(*registry);

// В коде продюсера/консьюмера
void push_task(Task t) {
    std::lock_guard<std::mutex> lock(mutex);
    queue.push(t);
    queue_size.Set(queue.size()); // обновляем метрику
}

2.2. Состояние пула потоков

Отслеживайте количество активных, ожидающих и заблокированных потоков. Для этого можно использовать std::thread::hardware_concurrency() и собственные счётчики:

auto& threads_active = BuildGauge()
    .Name("thread_pool_active_threads")
    .Help("Number of currently active threads")
    .Register(*registry);

// В момент старта/завершения задачи
threads_active.Set(active_thread_count);

2.3. Утечки ресурсов (дескрипторы, память)

С помощью перехвата new/delete или использования аллокаторов можно отслеживать количество живых объектов определённого типа:

auto& live_connections = BuildGauge()
    .Name("live_connections_total")
    .Help("Number of currently open connections")
    .Register(*registry);

class Connection {
public:
    Connection() { live_connections.Increment(); }
    ~Connection() { live_connections.Decrement(); }
};

3. Настройка алертов: от простых порогов до аномалий

Сбор метрик бесполезен без системы оповещений. Рассмотрим настройку алертов на примере Prometheus Alertmanager. Продвинутый уровень подразумевает не только статические пороги, но и динамические правила.

3.1. Статические алерты

Пример правила: если очередь задач превышает 1000 элементов в течение 5 минут — отправлять алерт.

groups:
  - name: cpp_app_alerts
    rules:
      - alert: HighTaskQueueLength
        expr: task_queue_size > 1000
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Queue length is high ({{ $value }})"

3.2. Динамические алерты на основе процентилей

Для гистограмм времени ответа можно использовать 95-й перцентиль:

      - alert: HighLatencyP95
        expr: histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) > 0.5
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "P95 latency is {{ $value }}s"

3.3. Алерты на основе скорости изменения

Отслеживание резкого роста ошибок (derivative):

      - alert: ErrorRateSpike
        expr: rate(http_requests_total{status="500"}[5m]) > 0.1
        for: 1m
        labels:
          severity: critical

4. Интеграция с Grafana: визуализация и дашборды

Финальный этап — построение информативных дашбордов в Grafana. Для C++ приложения рекомендуется создать отдельную панель для каждого компонента:

  • Общая панель: RPS, latency (p50, p95, p99), error rate.
  • Панель ресурсов: CPU, память, количество потоков, размер очередей.
  • Панель бизнес-метрик: количество обработанных заказов, активные пользователи и т.д.

Пример запроса для панели

Блоги

Рекомендаций по книгам