Мониторинг 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, память, количество потоков, размер очередей.
- Панель бизнес-метрик: количество обработанных заказов, активные пользователи и т.д.
Пример запроса для панели