Мониторинг работы приложения через OpenTelemetry
Когда приложение работает на сервере, снаружи не видно, что в нём происходит: почему медленно отвечает API, где «теряется» запрос и какой компонент стал причиной сбоя. OpenTelemetry (OTel) решает эту проблему — это открытый стандарт и набор библиотек для сбора трейсов, метрик и логов о работе приложения.
OTel подключается к приложению, фиксирует его работу — часто автоматически — и отправляет данные в систему хранения. При этом он не привязывает вас к конкретному вендору: данные можно экспортировать в Prometheus, OpenSearch и другие backend-системы, а анализировать и визуализировать их в Grafana.
Вы один раз внедряете мониторинг — и получаете понятную картину работы сервиса, быстрее находите узкие места и разбираете инциденты без привязки к одной платформе мониторинга.
Что показывают метрики, exemplars и логи
Для мониторинга Приложения (bs-backend) используется два основных вида данных: метрики и логи.
1. Метрики: сколько запросов в секунду обрабатывается, как долго они выполняются, сколько в них ошибок, сколько памяти и процессора занято. Они показывают, что происходит в целом.
2. Логи: текстовые записи о событиях. В каждую запись добавляется trace_id запроса.
Вместе с метриками передаются образцы (exemplars): отдельные реальные запросы, попавшие в данный интервал времени. Образец отображается на графике точкой, и чем выше точка, тем дольше выполнялся запрос. В нём есть trace_id, по которому в логах можно найти конкретный запрос, «испортивший» график.
Как данные проходят через систему:
1. Приложение с подключённым OTel SDK само отправляет метрики в Prometheus (push-модель, в отличие от привычной схемы, где Prometheus сам опрашивает приложение) и пишет логи с trace_id.
2. Prometheus хранит метрики вместе с образцами.
3. Fluentd собирает логи приложения и пересылает их в OpenSearch.
4. OpenSearch хранит и индексирует логи, по trace_id находятся все записи одного запроса.
5. Grafana показывает метрики из Prometheus и связанные с ними exemplars. По образцу можно перейти к поиску в OpenSearch по trace_id. Сами логи просматриваются в OpenSearch, а Grafana обеспечивает переход к ним с графика.
Это нужно для того, чтобы видеть состояние сервиса в реальном времени, быстро находить причину проблемы (от всплеска на графике до конкретного запроса и его логов) и замечать проблемы раньше пользователей.
Что нужно для подключения
1. Запущенный Prometheus с двумя флагами запуска:
command: - '--config.file=/etc/prometheus/prometheus.yml' - '--web.enable-otlp-receiver' # приём метрик по OTLP/HTTP - '--enable-feature=exemplar-storage' # хранение TraceId (Exemplars)
2. Сетевой доступ к Prometheus (соединение инициирует стенд).
3. Grafana с подключённым источником данных Prometheus (см. раздел Настройка Grafana).
Настройка отправки метрик в Prometheus
1. Настройка Endpoint
Для отправки метрик необходимо настроить Endpoint одним из следующих способов:
- Через переменные окружения (рекомендуется)
Этот вариант рекомендуется для Docker-развёртываний, поскольку позволяет задавать параметры подключения отдельно для каждого стенда, не изменяя конфигурацию приложения и не пересобирая образ. Например, для тестового и рабочего окружений можно указать разные адреса Prometheus через файл .env.
В .env файле укажите:
# Полный адрес приёмника метрик Prometheus (вместе с путём) OTEL_EXPORTER_OTLP_ENDPOINT=http://<адрес_prometheus>:<порт>/api/v1/otlp/v1/metrics # Для Prometheus всегда http/protobuf OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf # Как часто отправлять метрики, мс (по умолчанию минута, рекомендуем 15 секунд, т.е. 15000 мс) OTEL_METRIC_EXPORT_INTERVAL=15000 # Имя службы, по нему метрики различаются в Grafana OTEL_SERVICE_NAME=bs-backend
В docker-compose.yml в блок environment сервиса передайте эти переменные.
environment:
- OTEL_SERVICE_NAME=${OTEL_SERVICE_NAME}
- OTEL_EXPORTER_OTLP_ENDPOINT=${OTEL_EXPORTER_OTLP_ENDPOINT}
- OTEL_EXPORTER_OTLP_PROTOCOL=${OTEL_EXPORTER_OTLP_PROTOCOL}
- OTEL_METRIC_EXPORT_INTERVAL=${OTEL_METRIC_EXPORT_INTERVAL}
- Через appsettings.json (альтернатива)
Этот вариант подойдёт, если в используемом процессе развёртывания настройки приложения принято хранить в appsettings.json и удобнее управлять ими непосредственно в конфигурационном файле. Например, если конфигурация уже поддерживается в таком виде и не требуется переопределять параметры для разных окружений через переменные среды.
"OpenTelemetry": {
"ServiceName": "bs-backend",
"Otlp": {
"Endpoint": "http://<адрес_prometheus>:<порт>/api/v1/otlp/v1/metrics",
"Protocol": "HttpProtobuf"
}
}
Переменные окружения имеют приоритет над appsettings. Интервал отправки (OTEL_METRIC_EXPORT_INTERVAL) задаётся только переменной окружения.
2. Настройка работы с несколькими экземплярами
Можно добавить настройки для работы с несколькими экземплярами, если это требуется (например, bs-backend-1 и bs-backend-2).
Такая дополнительная настройка необходима, если:
- Требуется различать метрики нескольких экземпляров одного сервиса, использующих общий Prometheus.
- Требуется различать метрики нескольких стендов, использующих общий Prometheus.
Примечание! Если на стенде работает по одному экземпляру каждого сервиса и Prometheus используется только для него, то дополнительная настройка не требуется.
Задайте каждому свой OTEL_SERVICE_NAME, иначе метрики разных экземпляров сольются в один ряд. Если это реплики одной службы, оставьте одинаковое имя и различайте их так:
OTEL_RESOURCE_ATTRIBUTES=service.instance.id=bs-backend-1
Если Prometheus общий для нескольких стендов, добавьте окружение:
OTEL_RESOURCE_ATTRIBUTES=deployment.environment=stage
3. Перезапуск стенда для применения изменений
Перезапустите стенд с помощью скрипта bs_menu.sh. Проверьте, что переменные попали в контейнер:
docker exec <контейнер> printenv | grep OTEL
4. Проверка, что метрики начали считываться
Сделайте несколько запросов к приложению, подождите 15-60 секунд и откройте Prometheus (http://<адрес_prometheus>:<порт>), вкладка Graph:
http_server_request_duration_seconds_count
Должны появиться ряды с лейблом service_name="bs-backend".
Образцы можно проверить через API:
http://<адрес_prometheus>:<порт>/api/v1/query_exemplars?query=http_server_request_duration_seconds_bucket
В ответе должны быть объекты с trace_id. Образцы появляются только у запросов, для которых была создана трассировка, поэтому на малом числе запросов их может быть мало.
Настройка Grafana
Источник данных: Connections - Data sources - Add new data source - Prometheus.
- URL: адрес Prometheus.
- В разделе Exemplars нажмите Add: Label name = trace_id. Если логи находятся в Loki или Tempo, выберите его как Internal link. Если нет, укажите URL поиска по TraceId в вашей системе логов, подставив ${__value.raw} на место идентификатора.
- Нажмите Save & test.
Панель: Dashboards - New - Add visualization - выберите источник Prometheus, вставьте запрос и включите переключатель Exemplars в параметрах запроса. Без него точки на графике не появятся.
Какие графики полезно строить и как их читать
В документации OpenTelemetry, Prometheus и Grafana, а также в каталогах готовых дашбордов (например, Grafana Dashboards) можно найти множество графиков, построенных на метриках OpenTelemetry. Большинство из них рассчитаны на общие случаи и содержат много лишнего. Ниже приведены только самые полезные для мониторинга Business Studio: нагрузка, время ответа, ошибки и самые медленные маршруты. Для каждого графика указан запрос и описано, как читать результат.
Нагрузка: запросов в секунду (RPS)
sum by (service_name) (rate(http_server_request_duration_seconds_count[5m]))
Показывает, сколько запросов обрабатывает приложение.
Как читать: Резкий рост означает всплеск нагрузки, падение до нуля означает, что запросы не приходят или приложение недоступно. Смотрите вместе с графиком задержки: рост RPS и рост времени ответа вместе означает, что система упирается в ресурсы.
Время ответа (задержка): p50 / p95 / p99
histogram_quantile(0.95, sum by (le, service_name) (rate(http_server_request_duration_seconds_bucket[5m])))
Замените 0.95 на 0.50 (медиана) или 0.99 (самые медленные).
Как читать:
- p50 показывает, как работает приложение для обычного запроса.
- p95 / p99 показывают «хвост», то есть насколько плохо бывает самым неудачным запросам. Именно по ним пользователи замечают тормоза.
- Если p50 в норме, а p99 высокий, то тормозят отдельные запросы или операции, и в этом помогают образцы.
- Постепенный рост всех линий означает деградацию (нагрузка, рост данных, утечки).
- При малом трафике p95 и особенно p99 ненадёжны, потому что нужно хотя бы пара сотен запросов в окне, при меньшем количестве запросов значения могут заметно колебаться от одного интервала к другому, а отдельные медленные запросы — непропорционально сильно влиять на результат. В итоге график не всегда отражает реальную картину производительности сервиса. .
Образцы (Exemplars)
Дополнительная функция графика задержек - точечные маркеры поверх основного графика. Каждая точка соответствует реальному запросу из соответствующей корзины гистограммы. Чем выше точка, тем дольше он выполнялся.
Как пользоваться:
- Найдите на графике всплеск задержки.
- Наведите курсор на точку рядом с ним и скопируйте
trace_id. - Найдите этот TraceId в логах OpenSearch (либо по ссылке из точки, если она настроена). В логах будут все записи именно этого запроса, и видно, на каком шаге он замедлился.
- После изучения истории запроса расширьте поиск на временной диапазон вокруг него на том же bs-backend.Это поможет увидеть, какие ещё события происходили в этот момент и могли повлиять на время обработки запроса: ошибки, таймауты, задержки других запросов или проблемы зависимых сервисов.
Доля ответов с кодом ошибки
Доля ответов с кодом 5xx:
sum by (service_name) (rate(http_server_request_duration_seconds_count{http_response_status_code=~"5.."}[5m])) / sum by (service_name) (rate(http_server_request_duration_seconds_count[5m]))
Как читать: в норме линия около нуля. Любая устойчивая доля 5xx означает ошибки на стороне сервера, при этом нужно искать их в логах. Рост 4xx ("4..") чаще означает проблемы на стороне клиентов или авторизации. Код 499 (клиент оборвал соединение) лучше исключать из расчётов: http_response_status_code!="499"
Самые медленные маршруты
topk(5, histogram_quantile(0.95, sum by (le, http_route) (rate(http_server_request_duration_seconds_bucket[5m]))))
Показывает, какие именно методы API медленнее остальных (по лейблу http_route).
Типовые проблемы
| Симптом | Что проверить |
|---|---|
| В Prometheus нет метрик | Задан ли Endpoint (docker exec <контейнер> printenv | grep OTEL). Правильный ли адрес и порт. Есть ли сеть от стенда до Prometheus (nc -zv -w3 <адрес> <порт>) |
| Запрос к Endpoint возвращает 404 | В Prometheus не включён флаг –web.enable-otlp-receiver (для проверки: ответ 405 на GET означает, что приёмник включён) |
| Метрики есть, образцов нет | Нет флага –enable-feature=exemplar-storage, не включён переключатель Exemplars в запросе Grafana, или не настроен trace_id в источнике данных |
| Значения двух экземпляров суммируются и отображаются единой линией | У экземпляров одинаковые OTEL_SERVICE_NAME и нет различающего service.instance.id |
Ошибка compose unexpected type map | Смешаны форматы в environment (список и словарь). Пишите все строки как - KEY=value |
| Данные появляются с большой задержкой | Не задан OTEL_METRIC_EXPORT_INTERVAL, по умолчанию метрики отправляются раз в минуту |
Если Prometheus недоступен, приложение продолжает работать нормально: экспорт метрик просто пишет ошибки отправки в лог и повторяет попытки. Когда связь восстановится, передача данных восстановится автоматически.



