
Привет! Меня зовут Игорь Росляков, я технический писатель. По приглашению руководителя направления «Маркет и интеграции» Сергея Вострикова готовлю цикл статей на тему ИИ-ассистированной разработки решений для Битрикс24.
Сегодня настраиваем Observability для приложения Битрикс24 с помощью OpenTelemetry Collector. Для этого возьмём два готовых проекта:
приложение чат-бота, которое мы создали в одной из статей;
проект OpenTelemetry Backend, который лежит по адресу github.com/bitrix-tools/b24-ai-starter-otel.
В статье подключим телеметрию к нашему чат-боту и покажем, где можно следить за состоянием приложения и что там можно увидеть. Работать будем через агента — я использовал Codex.
О чём рассказывали в других статьях
В других статьях тоже много практических материалов, которые можно взять за основу и использовать в своём портале — например, их можно показать своим разработчикам или использовать самому.
Читать можно не по порядку:
Что будет в этой статье:
В чём идея отдельного проекта The OpenTelemetry (OTel) Collector
Содержание цикла статей про создание приложений с AI-агентами
Логирование, мониторинг и 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 проекта:
Готовое приложение — можно клонировать наш готовый проект чат-бот отсюда: github.com/igorrosliakov-bitrix24/Bitrix24-ChatBot
Репозиторий телеметрии, клонируем отсюда: github.com/bitrix-tools/b24-ai-starter-otel
Чтобы агенту было удобнее работать, помещаем оба проекта рядом в одном воркспейсе.
Для подключения проекта к порталу нужно зарегистрировать приложение в портале и прокинуть 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
test-otel-collector.sh отправляет HTTP POST на /v1/logs.
test-clickhouse.sh проверяет таблицы и считает записи.
test-grafana.sh проверяет API Grafana, datasource и дашборды.
test-integration.sh отправляет событие с уникальным test.id, потом ищет его в telemetry.otel_logs.
Скрипты генерации Данных
Папка 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 приложения
Чтобы приложение могло отправлять данные наружу, нужно указать три вещи:
Телеметрия включена.
По какому адресу находится Collector.
Какой токен нужно использовать для доступа к 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 правильно читает.
Полная цепочка:
Код приложения вызывает сервис телеметрии. Это момент, когда приложение понимает, что определённый момент надо зафиксировать. Приложение отправляет структурированное событие: имя события, тип, атрибуты.
OpenTelemetry SDK формирует логи и трейсы. Сервис внутри приложения использует OpenTelemetry SDK, а SDK превращает вызов из кода в стандартный OpenTelemetry-объект. Например, из
trackErrorполучается log record с error-атрибутами.После формирования события SDK должен куда-то его отправить. Этим занимается OTLP HTTP exporter. Он отправляет данные по HTTP на Collector:
http://host.docker.internal:4318/v1/logs
http://host.docker.internal:4318/v1/tracesCollector проверяет
Authorization token.Collector применяет processors. Это промежуточные шаги. Например, собирает события пачками, чтобы не отправлять каждую запись отдельно, или отсеивает ненужные записи.
Collector отправляет данные в ClickHouse. Для этого используется ClickHouse exporter. Он знает адрес ClickHouse, базу данных, логин и пароль.
ClickHouse сохраняет данные в
otel_logs / otel_traces. То есть хранилище принимает поток данных и складывает его в таблицы.Grafana подключается к ClickHouse и считывает данные SQL-запросами.
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:

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