Привет! Меня зовут Игорь Росляков, я технический писатель. По приглашению руководителя направления «Маркет и интеграции» Сергея Вострикова готовлю цикл статей на тему ИИ-ассистированной разработки решений для Битрикс24

Сегодня настраиваем Observability для приложения Битрикс24 с помощью OpenTelemetry Collector. Для этого возьмём два готовых проекта:

В статье подключим телеметрию к нашему чат-боту и покажем, где можно следить за состоянием приложения и что там можно увидеть. Работать будем через агента — я использовал Codex.

О чём рассказывали в других статьях

Что будет в этой статье:

Логирование, мониторинг и observability

Есть три уровня понимания системы.

Логирование отвечает на вопрос: «Что конкретно произошло?» Логи — это записи событий, которые происходят внутри приложения или инфраструктуры. Это как дневник событий, например:

User 123 logged in
Order 456 created
Database connection timeout
AI agent failed to call MCP tool
Webhook response: 500 Internal Server Error

Мониторинг отвечает на вопрос: «Все ли сейчас нормально?» Это наблюдение за заранее выбранными показателями системы: жив ли сервер, не закончилась ли память, отвечает ли база данных. Строится это обычно на метриках и можно сравнить с панелью приборов:

CPU usage: 82%
Error rate: 3.5%
Average response time: 430 ms
Requests per minute: 12 000
Queue size: 4 500 jobs

Observability отвечает на вопрос: «Почему система ведет себя именно так, даже если мы заранее не знали, какую проблему искать?» Это более широкое понятие: способность системы объяснить свое внутреннее состояние по внешним сигналам: логам, метрикам, трассировкам. Если мониторинг показывает заранее известные симптомы, то observability помогает расследовать неизвестные проблемы.

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

Frontend
  → API Gateway
    → Auth Service
      → Order Service
        → Payment Service
          → External Bank API

И тут можно увидеть, на каком именно шаге трейс замедлился или упал.

Когда есть Observability, специалист может получить ответы на важные вопросы. Например, где застревают пользователи? Сколько времени занимают вызовы? Где растёт задержка в ответах?

Для сбора логов, метрик и трейсов существует открытый стандарт OpenTelemetry. С ним приложение не привязано к определённой системе мониторинга. Код отправляет данные в OTLP-формате, а принять их может отдельный сервис OpenTelemetry Collector и передать в хранилище или систему визуализации.

В стартер-ките уже приготовлены переменные окружения для сервиса телеметрии в файле .env.example:

###> OpenTelemetry ###
# Единая точка включения/выключения телеметрии для backend и frontend.
# Используется PHP-бэкендом (TELEMETRY_ENABLED) и Nuxt-фронтом (NUXT_PUBLIC_TELEMETRY_ENABLED).
# OTel Collector должен быть запущен через b24-ai-starter-otel: cd ../b24-ai-starter-otel && make up
TELEMETRY_ENABLED=false
OTEL_EXPORTER_OTLP_ENDPOINT=http://host.docker.internal:4318
OTEL_EXPORTER_OTLP_HEADERS=Authorization=Bearer <generate-your-token>
OTEL_SERVICE_NAME=b24-app
OTEL_SERVICE_VERSION=1.0.0
OTEL_ENVIRONMENT=development
# Профиль телеметрии: simple-ui | integration-sync | integration-with-migration | migrator-light | migrator-advanced | development
OTEL_TELEMETRY_PROFILE=simple-ui
###< OpenTelemetry ###

Как это устроено: приложение на базе стартера отправляет телеметрию наружу, а b24-ai-starter-otel принимает, сохраняет и показывает в Grafana.

Это было про теорию, дальше будет практика.

В чём идея отдельного проекта The OpenTelemetry (OTel) Collector 

Сервис не должен сам знать все детали про системы мониторинга: ClickHouse, Grafana, Prometheus. Вместо этого приложение отправляет телеметрию в стандартном формате OpenTelemetry в отдельный проект The OpenTelemetry (OTel) Collector, и уже OTel Collector решает, что делать с этими данными дальше.

Зачем это надо: приложение становится независимее от конкретной системы наблюдаемости. Сегодня ты отправляешь данные в ClickHouse, завтра захочешь в Grafana Tempo, Datadog или Prometheus — приложение можно почти не трогать. Меняется в основном конфиг Collector.

Именно эту роль в сегодняшнем проекте выполняет проект b24-ai-starter-otel.

Подготовка

Нам нужно 2 проекта:

Чтобы агенту было удобнее работать, помещаем оба проекта рядом в одном воркспейсе.

Для подключения проекта к порталу нужно зарегистрировать приложение в портале и прокинуть HTTPS-туннель, чтобы Битрикс24 мог отправлять к проекту запросы. Как это сделать —  в инструкции под спойлером:

Регистрируем проект в портале

Зависимости

Что нужно установить перед работой:

  • Docker и Docker Compose для контейнеризации.

  • Git для контроля версий и пуша в удалённый репозиторий.

  • make для коротких команд.

  • Публичный HTTPS-туннель через CloudPub. Получить можно через десктопный клиент или инструмент командной строки clo.

  • Возможно, понадобится, но не наверняка: Node.js/pnpm и Xcode CLT для macOS toolchain.

Утилита make

Запускает готовые команды по коротким именам из файла Makefile. Чтобы поднять контейнеры или запустить мастер установки, понадобится одна короткая команда, которая под капотом может включать в себя длинный скрипт. Что можно сказать про make в разных системах:

  • На Linux часто уже есть или ставится пакетом.

  • На macOS приходит с Xcode Command Line Tools.

  • На Windows в чистом виде обычно нет, поэтому ставят через WSL2 или отдельно через пакеты.

На Windows вообще лучше работать через Linux-подсистему WSL2 (Ubuntu). Это потому, что проект и Docker-сценарии чаще ориентированы на Linux-окружение и могут работать в Windows не так стабильно.

Cloudpub, чтобы сделать приложение доступным по HTTPS

Для запуска приложения понадобится ключ API в сервисе CloudPub, который даёт локальному приложению публичный HTTPS-адрес. Он нужен как туннель между Битрикс24 и локальной разработкой. Получить ключ можно на главной странице личного кабинета после регистрации. 

После этого у вас появится технический домен CloudPub, который можно посмотреть в файле .env по значению VIRTUAL_HOST:

Регистрируем приложение на портале Битрикс24

Для подключения сервиса заходим на свой портал на bitrix24.ru в раздел Разработчикам:

После этого выбираем: Другое — Локальное приложение. Появится окно регистрации.

Сейчас нужно скопировать ваш технический домен и вставить его в таком виде в оба поля:

  • Путь вашего обработчика*: [технический-домен]/install

  • Путь для первоначальной установки: [технический-домен]/install

Нужно выбрать пункт «Использует только API», потому что у чат-бота нет фронтенда.

Чтобы приложение могло выполнять нужные действия на портале, ему нужно выдать соответствующие права. Для чат-бота это: task, tasks, tasks_extended, im, imbot, imconnector.

После сохранения появится окно, откуда нужно скопировать значения для ключей CLIENT_ID и CLIENT_SECRET для .env в корневой директории проекта: 

Сохраняем приложение и нажимаем «Переустановить».

Состав проекта b24-ai-starter-otel

Проект телеметрии состоит из нескольких основных компонентов.

Docker Compose

Главный манифест запуска проекта. Это инструкция для Docker: какие контейнеры поднять, какие порты открыть, какие папки подключить внутрь контейнеров и в каком порядке запускать сервисы.

Физически это один YAML-файл в корне проекта: docker-compose.yml. Docker Compose создает общую сеть telemetry-net. Внутри этой сети контейнеры видят друг друга по именам: collector может обращаться к clickhouse:9000, Grafana к clickhouse:8123.

Также compose подключает локальные файлы внутрь контейнеров. Например, otel-collector/config.yaml попадает внутрь collector как /etc/otel-collector-config.yaml, а grafana/provisioning попадает внутрь Grafana как готовая конфигурация datasource, dashboards и alerting.

OpenTelemetry Collector

Конфигурационный файл для готовой программы OpenTelemetry Collector. Он говорит, какие порты слушать, какие данные принимать, какие файлы логов читать. Ещё — фильтровать шум и отправлять всё в ClickHouse.

В конфиге есть четыре больших типа блоков: receivers, processors, exporters, extensions.

  • receivers — откуда брать данные. Здесь есть otlp для HTTP/gRPC событий и filelog/php, filelog/node, filelog/python для чтения файлов логов.

  • processors — что делать с данными по дороге. Например, batch собирает события пачками, attributes добавляет служебные поля, а filter/filelog_errors_only отбрасывает неважные debug/info записи из файловых логов. 

  • exporters — куда отправлять обработанные данные. Главный exporter здесь clickhouse, он пишет логи, трейсы и метрики в ClickHouse.

  • extensions — служебные возможности самого Collector. Они добавляют Collector дополнительные способности: healthcheck, авторизацию, диагностику.

ClickHouse

Папка clickhouse содержит SQL-файлы. Это не код, который выполняется постоянно. Это инструкции для базы данных: создать базу telemetry, создать таблицы, создать представления для аналитики.

ClickHouse играет роль большого склада событий. Всё, что пришло от приложения через collector, в итоге оседает в таблицах ClickHouse. Потом Grafana читает эти таблицы и строит графики, списки ошибок, воронки и панели.

Главная таблица — telemetry.otel_logs, она создается в 01_otel_logs.sql. В ней хранятся время события, сервис, уровень ошибки, текст сообщения и атрибуты.

Атрибуты — это дополнительные поля события. Например: portal.domain, event.name, error.class. Они лежат в LogAttributes как map-структура.

Есть еще materialized views: logs_analytics_mv и logs_support_mv. Они автоматически делают более удобные выборки из сырых логов: отдельно для продуктовой аналитики и отдельно для поддержки/ошибок.

Grafana

Папка grafana/provisioning — набор конфигураций для Grafana. Это инструкции для Grafana: какой datasource подключить, какие дашборды загрузить, какие алерты создать.

Datasource описан в grafana/provisioning/datasources/clickhouse.yaml. Там сказано: подключиться к ClickHouse по адресу http://clickhouse:8123, использовать базу telemetry.

Дашборды лежат JSON-файлами в grafana/provisioning/dashboards. Каждый JSON — это описание одного экрана Grafana: панели, SQL-запросы, переменные фильтрации, заголовки, графики.

Алерты Grafana

Папка grafana/provisioning/alerting отвечает за тревоги. Это конфигурации, которые говорят Grafana отправлять уведомления, если ошибок слишком много.

Grafana сама периодически выполняет SQL-запросы из alert rules и проверяет условия. Например: если за последние 5 минут появился FATAL, поднимаем critical alert.

Главный файл — alert-rules.yaml. В нем лежат SQL-запросы к ClickHouse и пороги.

Например, правило FATAL ошибки делает SELECT count() из telemetry.otel_logs за последние 5 минут, где SeverityText = 'FATAL'. Если результат больше 0, алерт срабатывает.

Файл contact-points.yaml описывает, куда слать уведомление. Сейчас там email. notification-policies.yaml описывает маршрутизацию: какие алерты куда отправлять.

Makefile

Файл с короткими командами для удобства:

  • make up создает папки data/clickhouse, data/grafana, data/otel-filepos, выставляет права и запускает docker-compose up -d.

  • make health проверяет collector, ClickHouse и Grafana.

  • make test-all запускает набор bash-тестов из папки tests.

  • make generate-data запускает скрипт генерации тестовой телеметрии. 

Тесты

Папка tests содержит bash-скрипты. Можно сказать, что это инфраструктурные тесты. Они проверяют, что работает вся цепочка:

событие → collector → ClickHouse → Grafana

Скрипты генерации Данных

Папка scripts содержит полезные bash-скрипты. Они нужны для проверки и демонстрации. То есть эти скрипты имитируют настоящее приложение: притворяются источником телеметрии и помогают проверить всю инфраструктуру без реального Битрикс24-приложения.

  • generate-test-data.sh создает искусственные события, например открытия и клики. Это нужно, чтобы Grafana не была пустой и можно было проверить дашборды.

  • trigger-alerts.sh специально создаёт плохие ситуации: много ошибок, fatal, ошибки на нескольких порталах. Это нужно для проверки алертов.

Оба скрипта формируют JSON в формате OTLP и отправляют его через curl на http://localhost:4318/v1/logs или /v1/traces.

События содержат атрибуты, которые потом используют SQL-запросы Grafana: event.name, event.source, portal.domain, portal.member_id, error.class, app.version.

Подключаем телеметрию

В отличие от нашего приложения, репозиторию телеметрии не обязательно нужен HTTPS-туннель. Битрикс24 должен видеть приложение через публичный HTTPS URL, а приложение должно видеть OTel Collector. Это два разных направления трафика, поэтому CloudPub нужен для приложения, но не всегда для OTel.

Схема работает так, что приложение отправляет логи, метрики и трейсы, а Collector их принимает, иногда обрабатывает и отправляет в нужное хранилище. Хранилище может меняться: сегодня данные уходят в ClickHouse, завтра в Grafana Tempo или Prometheus — но приложение можно почти не трогать. Меняется в основном конфиг Collector.

Для статьи я запускал оба проекта локально:

Если оба проекта запущены на одной машине, приложение может отправлять телеметрию в Collector локально: через Docker-сеть или через host.docker.internal:4318. Это то, что прописано в нашем .env-файле:

OTEL_EXPORTER_OTLP_ENDPOINT=http://host.docker.internal:4318

В .env-файле проекта должно стоять:

APP_LOGS_PATH=../b24-ai-starter/logs

Вместо b24-ai-starter нужно поставить название корневой папки вашего проекта.

Если приложение запущено не рядом с observability-проектом, а на другом сервере или в облаке, то OTel Collector тоже должен быть доступен снаружи. В этом случае всё равно может хватить одного CloudPub-агента, потому что CloudPub умеет публиковать несколько HTTP-ресурсов из одного контейнера/агента. 

Соединяем оба проекта

Обычно достаточно добавить проекты в один workspace и попросить агента проанализировать их и подключить телеметрию к приложению. После этого подключение может занять время, но в итоге выполняется успешно.

Дальше разберём, что делает агент на практике.

Проставление OTEL_COLLECTOR_AUTH_TOKEN

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

Это особенно важно, когда Collector доступен не только внутри локального Docker-окружения, а проброшен наружу или используется несколькими сервисами.

В проекте b24-ai-starter-otel в .env есть такая переменная:

OTEL_COLLECTOR_AUTH_TOKEN=...

В проекте приложения — у нас это b24-ai-starter — должен быть такой же токен, но в формате HTTP-заголовка:

OTEL_EXPORTER_OTLP_HEADERS=Authorization=Bearer <тот_же_токен>

Если токены не совпадают, приложение будет пытаться отправлять телеметрию, но Collector ее не примёт, а в логах появится ошибка 401 Unauthorized, или в Grafana не будет данных при включенной телеметрии.

.env приложения

Чтобы приложение могло отправлять данные наружу, нужно указать три вещи:

  1. Телеметрия включена.

  2. По какому адресу находится Collector.

  3. Какой токен нужно использовать для доступа к Collector.

В .env приложения нужны примерно такие значения:

TELEMETRY_ENABLED=true
OTEL_EXPORTER_OTLP_ENDPOINT=http://host.docker.internal:4318
OTEL_EXPORTER_OTLP_HEADERS=Authorization=Bearer <Ваш токен>
OTEL_SERVICE_NAME=b24-app
OTEL_SERVICE_VERSION=1.0.0
OTEL_ENVIRONMENT=development
OTEL_TELEMETRY_PROFILE=simple-ui

Обратите внимание на host.docker.internal. Приложение работает внутри Docker-контейнера, а Collector запущен в соседнем Docker Compose проекте. Через host.docker.internal контейнер приложения может обратиться к сервису, опубликованному на хост-машине.

Если написать localhost:4318 внутри контейнера приложения, это почти всегда будет ошибкой. Для контейнера localhost — это сам контейнер, а не компьютер и не соседний compose-проект.

.env OTel Collector

В Observability-проекте нужно подготовить токен Collector, пароли ClickHouse/Grafana, путь к логам приложения, если Collector будет читать их из файлов.

В b24-ai-starter-otel/.env важны такие вещи:

OTEL_COLLECTOR_AUTH_TOKEN=...
APP_LOGS_PATH=../b24-ai-starter/logs
CLICKHOUSE_PASSWORD=...
GRAFANA_ADMIN_PASSWORD=...

APP_LOGS_PATH нужен, если Collector читает файловые логи приложения через filelog receiver. Потому что приложение может само сформировать OpenTelemetry-событие и отправить его по сети:

Приложение → 

 → http://host.docker.internal:4318/v1/logs → 

 → OTel Collector → 

 → ClickHouse

Но у PHP/Symfony-приложения есть и обычные файловые логи. Например:

b24-ai-starter/logs/php/symfony/dev.log

Приложение пишет сюда свои стандартные события об ошибках, предупреждениях, исключениях. Но в проекте OTel Collector запускается в своем Docker-контейнере и сам по себе не видит файлы соседнего проекта. Поэтому мы говорим Docker взять папку b24-ai-starter/logs на хост-машине и подключить ее внутрь контейнера Collector. Переменная APP_LOGS_PATH указывает, где на машине лежат логи приложения:

APP_LOGS_PATH=../b24-ai-starter/logs

Получается два канала. Приложение может отправлять структурированные события через OTLP — осмысленные события, которые мы сами решили отслеживать. Дополнительно Collector может подбирать ошибки из logs/php.

Как работает наблюдаемость

В правильно работающей системе получается цепочка: приложение создаёт событие, Collector принимает, ClickHouse сохраняет, а Grafana правильно читает.

Полная цепочка:

  1. Код приложения вызывает сервис телеметрии. Это момент, когда приложение понимает, что определённый момент надо зафиксировать. Приложение отправляет структурированное событие: имя события, тип, атрибуты.

  2. OpenTelemetry SDK формирует логи и трейсы. Сервис внутри приложения использует OpenTelemetry SDK, а SDK превращает вызов из кода в стандартный OpenTelemetry-объект. Например, из trackError получается log record с error-атрибутами.

  3. После формирования события SDK должен куда-то его отправить. Этим занимается OTLP HTTP exporter. Он отправляет данные по HTTP на Collector:
    http://host.docker.internal:4318/v1/logs
    http://host.docker.internal:4318/v1/traces

  4. Collector проверяет Authorization token.

  5. Collector применяет processors. Это промежуточные шаги. Например, собирает события пачками, чтобы не отправлять каждую запись отдельно, или отсеивает ненужные записи.

  6. Collector отправляет данные в ClickHouse. Для этого используется ClickHouse exporter. Он знает адрес ClickHouse, базу данных, логин и пароль. 

  7. ClickHouse сохраняет данные в otel_logs / otel_traces. То есть хранилище принимает поток данных и складывает его в таблицы. 

  8. Grafana подключается к ClickHouse и считывает данные SQL-запросами.

  9. Dashboard показывает события, ошибки, latency.

Как это выглядит

После этого можно открывать Grafana по адресу: http://localhost:3001

Откроется окно входа:

Данные для входа можно посмотреть в .env-файле Collector.

В разделе Dashboards можно выбрать, какие события мы хотим просмотреть:

Наш чат-бот умеет получать по внешнему API курсы акций. Если дать ему команду, он ответит:

А на дашборде появится запись о событии:

Теперь попробуем вызвать простую ошибку бэкенда на нашем приложении. Дадим боту неправильную команду, которую он умеет обрабатывать, но возвращает ошибку:

Открываем наши дашборды и видим ошибку почти в то же время, которое отображается в браузере:

В ClickHouse можно посмотреть конкретную строку ошибки, если в терминале Collector выполнить команду:

docker compose exec clickhouse clickhouse-client --query="
SELECT
  Timestamp,
  ServiceName,
  SeverityText,
  Body,
  LogAttributes['event.name'] AS event,
  LogAttributes['error.class'] AS error_class,
  LogAttributes['error.message'] AS error_message
FROM telemetry.otel_logs
WHERE ServiceName = 'b24-app'
ORDER BY Timestamp DESC
LIMIT 20
" --format=PrettyCompact

Это выведет примерно такой результат:

Можно запустить демонстрационную телеметрию, чтобы посмотреть, как в Grafana отображаются события разного типа. Для этого в терминале приложения чат-бота выполняем команду:

TOKEN=$(curl -sS -X POST https://repressively-fertile-verdin.cloudpub.ru/api/getToken \
  -H "Content-Type: application/json" \
  -d '{"DOMAIN":"demo.bitrix24.ru","member_id":"article-demo-member"}' \
  | jq -r '.token')
echo "$TOKEN"

После этого можно запускать демонстрацию:

curl -sS https://repressively-fertile-verdin.cloudpub.ru/api/telemetry/test \
-H “Authorization: Bearer $TOKEN” \
| jq

Терминал покажет примерно такой ответ:

  "status": "ok",
  "fired_events": [
    "app_opened",
    "api_list_called",
    "bitrix_api_call",
    "b24_event_action_initiated",
    "b24_event_processed",
    "screen_view",
    "trackError(RuntimeException)",
    "trackOperation(test.simple_operation)",
    "trackOperation(test.inner_operation)",
    "trackOperation(test.outer_operation)"
  ],
  "fired_count": 10,
  "session_id": "54137a9a-500f-4653-a595-e0c1d8e1e192",
  "portal_domain": "demo.bitrix24.ru"

А на дашбордах появится много разных записей:

Правила алертов можно задать прямо в Grafana в разделе Alerting:

Что будем делать дальше

У нас есть подключенные метрики и логи. В следующий раз покажем и расскажем, как на базе собираемой информации строить технический мониторинг и продуктовые отчеты.

Содержание цикла статей про создание приложений с AI-агентами

  1. Пишем первое приложение с AI-стартером, чтобы видеть прибыли и убытки

  2. Добавляем в бизнес-портал Битрикс24 роботов для автоматизации

  3. Что даёт воспроизводимая среда разработки и как развернуть контейнеры на VPS

  4. Анализ и модернизация коннектора баз данных с помощью AI-агентов

  5. Создание чат-бота в портале Битрикс24 с помощью AI-агентов

  6. Как стартер-кит может стать стандартом разработки

Комментарии (0)