
Привет! Меня зовут Игорь Росляков, я технический писатель. По приглашению руководителя направления «Маркет и интеграции» Сергея Вострикова готовлю цикл статей на тему ИИ-ассистированной разработки решений для Битрикс24.
Сегодня посмотрим на практике, как работать с техническими метриками и продуктовыми сигналами, если у вас есть приложение для портала Битрикс24. Из чего состоит работа и что будем делать:
Первая часть проекта — готовое приложение чат-бота, которое подключено к моему порталу. Его можно посмотреть здесь: github.com/igorrosliakov-bitrix24/Bitrix24-ChatBot
Вторая часть — проект телеметрии, который специально создан для подключения Observability к кастомизациям Битрикс24: github.com/bitrix-tools/b24-ai-starter-otel
Мы уже подключили репозиторий телеметрии к репозиторию приложения. Телеметрия собирает метрики и показывает дашборды через Grafana. Теперь нам нужно понять, как получать из метрик полезную для бизнеса информацию: как пользователи работают с приложением, какими функциями пользуются и где сталкиваются с ошибками.
Проект приложения чат-бота основан на готовом шаблоне — AI-стартере для разработки с ИИ-агентами github.com/bitrix-tools/b24-ai-starter. И в стартер, и в проект телеметрии уже вшиты инструкции для работы с ИИ. Поэтому всю работу будем делать через агента — я использовал Codex.
Что мы уже делали с проектом ИИ-стартера
В других статьях тоже много практических материалов, которые можно взять за основу и использовать в своём портале — например, их можно показать своим разработчикам или использовать самому.
Читать можно не по порядку:
Пишем первое приложение с AI-стартером, чтобы видеть прибыли и убытки
Добавляем в бизнес-портал Битрикс24 роботов для автоматизации
Что даёт воспроизводимая среда разработки и как развернуть контейнеры на VPS.
Анализ и модернизация коннектора баз данных с помощью AI-агентов
OTel Collector в кастомизации Битрикс24: подключаем Observability
Технический и продуктовый мониторинг за кастомизациями Битрикс24: как настроить и что искать (вы здесь)
Что будет в этой статье:
Что нужно для составления полезных отчётов с продуктовыми сигналами
Содержание цикла статей про создание приложений с AI-агентами
Что нужно для повторения нашего проекта
Понадобятся работающее, подключенное к вашему порталу приложение, и подключенный репозиторий телеметрии.
Можно скопировать готовый проект чат-бота и добавить его в ваш аккаунт. Все зависимости и алгоритм действий для регистрации в портале подробно описаны в предыдущей статье в разделе «Подготовка»:

Самый простой способ подключить телеметрию — клонировать репозиторий b24-ai-starter-otel и поместить его рядом с проектом приложения локально в одно рабочее пространство:

После этого чаще всего достаточно попросить агента подсоединить телеметрию к приложению. Если вам интересно подробнее, как это будет работать, можно почитать нашу предыдущую статью про подключение Observability.
Что уже есть и что сделаем сегодня
Сначала разберёмся, что есть в проекте сейчас, и как будем двигаться.
У нас 2 проекта, которые работают в связке.
Проект чат-бота подключен к порталу. Бот умеет выдавать котировки акций, курсы валют и список задач. Писать ему нужно в мессенджере:

Проект телеметрии подключен к боту и умеет собирать некоторые показатели. Набор дашбордов в Grafana выглядит так:

Телеметрия не связана с порталом Битрикс24 и в нашем случае работает локально на нашей машине через Docker. Другой вариант запуска — в вашем приложении при подключении внешнего сервиса телеметрии. Приложение общается с порталом и отдаёт метрики в телеметрию, а телеметрия показывает результаты в Grafana.
В статье мы добавим несколько оптимизаций, построим новый дашборд и покажем, как можно собирать информацию о работе сервисов для бизнес-анализа.
Что нужно для составления полезных отчётов с продуктовыми сигналами
В процессе мониторинга мы собираем технические метрики и продуктовые события-сигналы.
Технические метрики отвечают на вопрос о том, как работает приложение: сколько произошло ошибок, дошёл ли вебхук, сколько запросов вернули ошибку 500. По техническим метрикам мы понимаем, работает ли приложение стабильно.
Продуктовые события-сигналы показывают, дошёл ли пользователь до ценности продукта. Это больше полезно маркетингу, аналитикам, менеджерам, владельцу сервиса. Продуктовые события могут показать, что пользователь установил бота, вызвал определённую команду, начал работать с ботом регулярно. Эти вещи показывают вовлечённость.
Продуктовых сигналов может быть любое количество. Это зависит от целей владельца приложения. Например, в мониторинг можно добавить такое:
chatbot_joined_chat— пользователь открыл/добавил бота в чат.chatbot_message_received— пользователь написал сообщение боту.chatbot_command_used— бот понял команду: /stock, /fx, /market, /morning, help.chatbot_activation_completed— пользователь получил первый успешный полезный ответ.chatbot_repeat_usage— пользователь вернулся/написал не первый раз.chatbot_regular_usage— пользователь стал пользоваться регулярно.
Полезные бизнес-гипотезы чаще строятся не по одному событию, а по связкам сигналов. Чаще это связки разных сигналов, по которым можно сделать какие-то предположения.
Например, мы видим, что пользователь установил приложение, но не написал боту. Можно выдвинуть гипотезу: людям не понятно, где найти бота, поэтому нужен onboarding или понятная кнопка «Открыть чат с ботом».
Или такой пример: пользователь вызывает только команду курса акций и валют /stock, но не пользуется списком задач /morning. Гипотеза такая: ценность финансовых котировок понятна, а дайджест задач не очевиден.
Составляем план
Сначала мы с агентом разобрались в режиме планирования, что будем понимать под техническими метриками и продуктовыми событиями и что стоит добавить в нашего бота для демонстрационных целей.
После этого я попросил ИИ составить план и сохранить его в отдельный .md-файл, чтобы потом сверяться с ним перед каждым шагом. Спойлер — получалось в целом успешно, но не 100%, потому что иногда агент забегал вперёд и брал что-то из следующего этапа. Но это было не слишком критично, и в целом агент всё же следовал намеченной траектории развития проекта.
Вот несколько примеров событий, которые предложил добавить агент:
chatbot_joined_chat— пользователь открыл или добавил бота в чат.chatbot_message_received— бот получил пользовательское сообщение.chatbot_command_used— бот распознал команду.chatbot_unknown_command— пользователь написал то, что бот не понял.
Кроме этих сигналов, по истории событий в Grafana будут считаться показатели регулярности работы пользователя с ботом. Формулы будут упрощённые, но идея реальная: видеть, как часто пользуются приложением.
Этап 1: добавляем записи о важных событиях
Сначала мы сделали так, чтобы бот начал оставлять дополнительные записи в телеметрии. Приложение уже умело фиксировать установку приложения и регистрацию бота. То есть мы видели начало пути:
app_installed -> chatbot_registered
Но дальше была слепая зона: мы не видели, открыл ли пользователь чат, написал ли боту, получил полезный ответ или столкнулся с ошибкой. Поэтому агент добавил события, которые описывают поведение пользователя после регистрации бота.
Главный файл этапа — контроллер в боте, который находится в /backends/php/src/Controller/ChatbotEventsController.php. Он принимает вебхук от Битрикс24, определяет тип события или команду пользователя, вызывает нужную бизнес-логику и отправляет ответ в чат. После этого этапа он начал описывать каждый важный переход с помощью телеметрии.
Верхнеуровнево скрипт состоит из четырех частей:
1. process() управляет полным сценарием обработки вебхука.
2. classifyMessageForTelemetry() превращает сообщение в безопасные аналитические признаки.
3. Методы build*Reply() формируют ответы для команд help, stock, fx, market и morning.
4. sendReply() отправляет ответ в Битрикс24 и записывает результат отправки.
Вызовы телеметрии встроены в уже существующий сценарий обработки сообщения. Поэтому событие появляется только тогда, когда соответствующее действие действительно произошло. Упрощённо главная часть выглядит так:
// фиксируем технический факт: Битрикс24 доставил webhook приложению $this->telemetry->trackEvent( 'chatbot_webhook_received', $baseTelemetryAttributes, ); // извлекаем сообщение для бизнес-логики, но не отправляем его текст в телеметрию $message = $this->extractUserMessage($payload); // заменяем исходный текст безопасным набором признаков $commandAttributes = array_merge( $baseTelemetryAttributes, $this->classifyMessageForTelemetry($message), ); // фиксируем продуктовое действие: пользователь написал боту $this->telemetry->trackEvent( 'chatbot_message_received', $commandAttributes, ); // разделяем распознанные и неизвестные команды $this->telemetry->trackEvent( 'unknown' === $commandAttributes['chatbot.command'] ? 'chatbot_unknown_command' : 'chatbot_command_used', $commandAttributes, );
TelemetryInterface здесь является общим входом в уже подключенную систему OpenTelemetry. Контроллер сообщает ей название события и набор атрибутов, а дальнейшая отправка в OTel Collector остается задачей инфраструктурного слоя приложения.
Теперь общая схема использования выглядела так. Когда Битрикс24 присылает вебхук боту, приложение фиксирует:
chatbot_webhook_received
Это значит, что Битрикс24 действительно постучался к нашему приложению. Когда пользователь открывает или добавляет бота в чат, появляется:
chatbot_joined_chat
Когда пользователь пишет сообщение, поступает другой сигнал:
chatbot_message_received
Дальше приложение пытается понять, какую команду ввёл пользователь. Например, /help или /market AAPL MSFT USD EUR. Если команда понятна, фиксируется chatbot_command_used. Если нет, фиксируется chatbot_unknown_command.
Это важно для продуктовой аналитики. Если записей unknown_command слишком много, то пользователи ожидают от бота чего-то другого, не того, что мы закладывали. Например, люди пишут свободным текстом, а бот рассчитан на команды. Это уже маркетинговая или продуктовая гипотеза.
Когда бот успешно отправил в Битрикс24 ответ, фиксируется chatbot_reply_sent. Если не смог отправить ответ, то chatbot_reply_failed.
А когда пользователь получил первый по-настоящему полезный ответ, например на /market, /stock, /fx или /morning, фиксируется chatbot_activation_completed. То есть мы начинаем видеть ту точку, где клиенты или сотрудники начали получать какой-то реально полезный эффект.
Ещё мы специально не сохраняем сырой текст сообщения. Это важно, потому что в телеметрию не нужно отправлять пользовательские фразы целиком. Например, пользователь пишет: /market AAPL MSFT USD EUR. Мы не сохраняем эту строку целиком как текст сообщения. Вместо этого сохраняем признаки, которые могут выглядеть так:
chatbot.command = market
chatbot.has_arguments = true
chatbot.message_length_bucket = 21-100
В коде это выглядит так:
return [ // единое имя команды позволяет группировать разные варианты написания 'chatbot.command' => $canonicalCommand, // тип показывает, использует человек команды или пишет свободным текстом 'chatbot.message_type' => $messageType, // сохраняем факт наличия аргументов, но не сами аргументы 'chatbot.has_arguments' => [] !== $arguments ? 'true' : 'false', // количество аргументов полезно для анализа сложности запросов 'chatbot.arguments_count' => (string) count($arguments), // точную длину заменяем диапазоном 'chatbot.message_length_bucket' => $this->messageLengthBucket($message), ];
Теперь можно построить нормальный отчёт. Мы будем знать, сколько людей вызвали market, сколько вызвали help, сколько писали непонятные команды и другие понятные бизнесу результаты. Так мы можем строить аналитику, но одновременно не складываем в ClickHouse лишние пользовательские данные. В результате у нас появляется связная история, которая будет выглядеть примерно так:
Портал установил приложение →
Бот зарегистрировался →
Пользователь открыл чат →
Пользователь написал help →
Бот успешно ответил.
Этап 2: определяем атрибуты, которые будем фиксировать
Это был как раз тот случай, когда агент уже реализовал всё запланированное в предыдущем шаге. Чтобы не бездельничать, ИИ немного оптимизировал наше приложение тестами и добавил несколько проверок:
Содержит ли UIProfile нужные chatbot.* атрибуты.
Пропускает ли simple-ui безопасные признаки бота.
Отфильтровывается ли сырой текст сообщения, например chatbot.message_text.
Технически это реализовано так. Агент изменил 3 файла в проекте бота:
/backends/php/src/Service/Telemetry/Profiles/UIProfile.php/backends/php/tests/Telemetry/Profiles/UIProfileTest.php/backends/php/tests/Telemetry/FilteringTest.php
UIProfile.php содержит разрешенный список атрибутов для профиля simple-ui. Это фильтр перед отправкой: если ключ не разрешен профилем, телеметрический сервис удалит его из события. Тесты отвечают за две разные проверки: UIProfileTest проверяет состав самого разрешенного списка, а FilteringTest пропускает через реальный фильтр смешанный набор данных и проверяет итог.
В профиль добавлена отдельная группа:
return [ // другие группы атрибутов профиля // идентифицирует бота и исходный тип события Битрикс24 'chatbot.code', 'chatbot.event', // описывает использование без сохранения текста сообщения 'chatbot.command', 'chatbot.message_type', 'chatbot.has_arguments', 'chatbot.arguments_count', 'chatbot.message_length_bucket', // описывает результат и скорость ответа 'chatbot.reply_status', 'chatbot.reply_duration_ms', ];
Если ChatbotEventsController добавит разрешённый ключ chatbot.command, он пройдет дальше. Если кто-то позже попробует добавить chatbot.message_text, фильтр его отбросит, потому что такого ключа в списке нет.
Дополнительно тестируем не только наличие полезных полей, но и отсутствие опасных:
$attributes = [ // безопасный аналитический признак 'chatbot.command' => 'market', // исходные пользовательские данные, которые не должны пройти 'chatbot.message_text' => '/market AAPL MSFT USD EUR', 'chatbot.raw_payload' => '{"MESSAGE":"/market AAPL MSFT USD EUR"}', ]; // моделируем фильтрацию перед отправкой события в OTel Collector $filtered = $manager->filterAttributes($attributes); // команда остается доступной для отчета $this->assertArrayHasKey('chatbot.command', $filtered); // сырой текст и исходный payload удаляются $this->assertArrayNotHasKey('chatbot.message_text', $filtered); $this->assertArrayNotHasKey('chatbot.raw_payload', $filtered);
Так второй этап закрепляет правило из первого этапа технически: полезные признаки должны пройти, а пользовательский текст — нет.
Этап 3: расширяем технический мониторинг
Здесь мы стали точнее понимать, почему бот может не работать. На первых шагах мы начали видеть пользовательские действия: пользователь написал боту, бот понял команду и отправил ответ.
Но для технического мониторинга этого мало. Пользователь мог написать /market AAPL, а ответ не пришёл или пришёл медленно. Нужно понять, где проблема: это вебхук не дошёл или бот не понял команду? Или тормозит внешний API котировок? Например, предыдущая версия бота использовала сервис stooq.com, и в какой-то момент API перестал отвечать.
Теперь каждая важная внешняя операция измеряется отдельно. Например, команда /market AAPL MSFT USD EUR вызывает несколько зависимостей:
Получить котировку AAPL.
Получить котировку MSFT.
Получить курс USD/EUR.
Отправить ответ в чат Битрикс24.
Теперь по каждой такой операции можно увидеть, какая система и какой метод вызывались, прошло это успешно или с ошибкой и сколько времени заняло.
То есть до этого мы могли узнать только то, что бот не ответил. А теперь стало намного подробнее. Мы видим, например, что бот получил сообщение, понял команду и получил котировки, но API Битрикс24 imbot.message.add вернул ошибку.
На практике это может применяться так. В продуктовой аналитике видно, что пользователь начал работать с ботом, но не продолжил: first_message есть, а repeat_message нет. Тогда мы идём в технический мониторинг и смотрим, был ли ответ успешным, быстрым, не падал ли внешний API и не было ли ошибок Битрикс24. Далеко не факт, что причина в технике, но так у нас будет полная картина для понимания пользовательского опыта.
Код менялся в том же файле-контроллере, который агент менял на 1-м этапе: ChatbotEventsController.php. Чтобы измерять каждый внешний вызов отдельно, мы добавили новый главный метод — trackMeasuredDependencyCall(). Он используется для получения курсов валют, котировок и задач через API Битрикс24. Отправка готового ответа через imbot.message.add измеряется отдельно в sendReply().
Добавился единый измеритель внешних зависимостей:
private function trackMeasuredDependencyCall( string $systemName, string $operationType, string $apiMethod, array $telemetryAttributes, callable $operation, ): mixed { // запускаем отдельный таймер именно для этой зависимости $startedAt = hrtime(true); // эти поля объясняют, какую систему и какую операцию мы измеряли $attributes = array_merge($telemetryAttributes, [ 'external.system_name' => $systemName, 'external.operation_type' => $operationType, 'api.method' => $apiMethod, ]); try { // выполняем исходный вызов внутри trace-span $result = $this->telemetry->trackOperation( 'dependency.' . $apiMethod, $operation, $attributes, ); // записываем успешный вызов и его длительность для Grafana $this->telemetry->trackEvent('external_api_call', array_merge( $attributes, [ 'api.status' => 'success', 'api.duration_ms' => (string) $this->elapsedMs($startedAt), ], )); return $result; } catch (\Throwable $throwable) { // при ошибке сохраняем тот же контекст и длительность $this->telemetry->trackEvent('external_api_call', array_merge( $attributes, [ 'api.status' => 'error', 'api.duration_ms' => (string) $this->elapsedMs($startedAt), ], )); // исключение пробрасывается дальше, поэтому поведение приложения не маскируется throw $throwable; } }
Здесь callable $operation — переданная функция с реальным API-вызовом. Благодаря этому один helper измеряет разные зависимости одинаково, а код построения ответа не дублирует обработку времени и ошибок.
Этап 4: добавляем новый дашборд
Первоначально шаг назывался «Что изменить в коде», но к этому моменту агент уже добавил все нужные изменения на предыдущих шагах. Поэтому вместо кода агент добавил новый дашборд в Grafana: Chatbot Product Funnel & Technical Health. Это пример того, как может быть составлен полезный сценарий.
Дашборды лежат grafana/provisioning/dashboards в виде JSON-файлов: product-analytics.json, conversion-funnel.json, errors-overview.json. Grafana настроена так, что читает эту папку с помощью манифеста dashboards.yaml.
Настройка панели внутри dashboard — это SQL-запрос к ClickHouse. Например:
SELECT count() FROM telemetry.otel_logs WHERE LogAttributes['event.name'] = 'chatbot_message_received'
То есть Grafana здесь — визуальный слой поверх ClickHouse. Сначала всё происходящее сохраняется как строки данных. А для удобного быстрого анализа нужна визуализация.
На новом дашборде есть несколько полезных частей.
Путь пользователя. Это запись успешных последовательных действий:
установка -> бот зарегистрирован -> первое сообщение -> повторное сообщение -> регулярное использование
Обратите внимание, что мы для статьи использовали упрощённые показатели «повторное сообщение» (пользователь написал боту дважды) и «регулярное использование» (три раза и более). В реальных сценариях можно задать более сложные формулы, лучше отвечающие задачам бизнеса. Наши последние 3 шага считаются так:
first_message = есть 1+ chatbot_message_received
repeat_message = есть 2+ chatbot_message_received
regular_usage = есть 3+ chatbot_message_received
Timeline показывает события с временными метками, что может быть особенно удобно. Например, 10:01 app_installed или 10:05 chatbot_command_used / market.
Техническое здоровье. Это ответы на вопросы технических метрик: пришёл ли вебхук вообще, смог ли бот отправить ответ, падала ли обработка запроса. Техническое здоровье помогает объяснить проблемы продуктовой воронки, когда пользователи не пользуются продуктом или отдельными функциями.
Если новый дашборд показывает, как пользователь проходит путь работы с ботом, то остальные дашборды отвечают на более широкие вопросы. Например, Errors Overview показывает, что сломалось и на каком портале, а API Performance разбирает производительность вызовов API Битрикс24.
Этап 5: проверяем работу продуктового мониторинга
Агент сделал нам простой дашборд для демонстрации продуктовых событий-сигналов. Это воронка, которая может показать вещи вроде такой: пользователь написал первое сообщение, но не вернулся. Тогда мы открываем другие дашборды и проводим исследование, почему так получилось.
Для проверки работоспособности бота я переустановил приложение в портале и заново проставил секреты в .env-файле. Если у вас приложение тоже установлено, а вы хотите проверить все шаги, начиная с установки, нужно сделать так же.

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

Первый шаг пройден: app_installed = 1. Это значит, что приложение открылось в режиме установки, получило OAuth/auth payload от Битрикс24 и записало факт установки.
Но видно, что установка не завершилась. Нам нужен следующий сигнал, app_install_finalized. Он появится, когда Битрикс24 пришлёт lifecycle-событие ONAPPINSTALL нашему приложению. Событие должно прийти на эндпойнт /api/app-events/. Тогда бэкенд обрабатывает событие и присылает в телеметрию app_install_finalized.
Чтобы узнать, почему этого сигнала не было в конце установки, запускаем терминал для проекта приложения и посылаем в терминал команду:
docker compose --env-file .env logs api-php --tail=100.
После этого читаем полученные логи или отправляем их агенту и запрашиваем объяснение. В моём случае агент сам посмотрел логи ClicksHouse и нашёл такие ключевые ошибки:
duplicate key value violates unique constraint "unique_b24_user_domain" Key (b24_user_id, domain_url)=(1, b24-w9apyi.bitrix24.ru) already exists.
Это значит, что приложение не смогло сохранить портал и пользователя в локальную базу данных из-за того, что там остались записи от прошлой установки. Это потому, что я уже запускал этот же проект на своём портале.
Решить это можно разными способами, но я выбрал самый простой и попросил агента сбросить данные локальной БД. Так можно делать, если записями в базе можно пожертвовать. Тогда записи обнулятся:
application_installation = 0 bitrix24account = 0
После этого повторная установка в портале прошла полностью успешно:

Чтобы найти бота, нужно открыть мессенджер и начать вводить его имя — оно задано константой в AppLifecycleController.php:
private const CHATBOT_NAME = 'Task Radar';
Вводим имя на портале:

Начнём с ознакомительной команды, которая объяснит возможности бота:

Бот успешно ответил согласно сценарию, значит, на дашборде это должно отобразиться:

Попробуем отправить следующее сообщение и запросить курсы акций и валют. Бот возвращает ответ, но пишет, что курсы акций сейчас недоступны:

Сообщение возвращено успешно, поэтому в метриках появился следующий законченный этап:

Перед последним шагом я попросил агента поменять API для получения курса акций. Теперь сначала мы отправляем запрос на Alpha Vantage и Yahoo Finance, а если оба сервиса не отвечают, в последнюю очередь пробуем Stooq, который в моём случае перестал отвечать.
Отправляем третье сообщение боту и получаем полный ответ:

Но при этом у нас сломался таймлайн в Grafana:

Если нажать на таймлайн, можно получить описание ошибки:
A Status: 500. Message: error querying the database: sendQuery: [HTTP 500] response body: "Code: 10. DB::Exception: Not found column LogAttributes in block. There are only columns: and(greaterOrEquals(Timestamp, toDateTime(1781252755)), lessOrEquals(Timestamp, toDateTime(1781256355)), in(arrayElement(LogAttributes, 'event.name'), ('app_installed', 'app_install_finalized', 'event_subscription_registered', 'chatbot_registered', 'chatbot_joined_chat', 'chatbot_message_received', 'chatbot_command_used', 'chatbot_unknown_command', 'chatbot_activation_completed', 'chatbot_reply_sent', 'chatbot_reply_failed', 'chatbot_processing_completed', 'chatbot_processing_failed')), or(equals('__all__', '__all__'), equals(arrayElement(LogAttributes, 'portal.domain'), '__all__'))), arrayElement(LogAttributes, 'portal.domain'), arrayElement(LogAttributes, 'event.name'), Timestamp: While executing MergeTreeSelect(pool: ReadPool, algorithm: Thread). (NOT_FOUND_COLUMN_IN_BLOCK) (version 23.12.6.19 (official build)) "
Суть в том, что ClickHouse споткнулся на запросе, где агент одновременно выбирал поля из LogAttributes[...], фильтровал по ним и сортировал по Timestamp. Когда агент поправил запрос, Grafana снова начала работать нормально:

Смотреть события портала можно не только в Grafana, но и в командной строке. Например, таким точечным запросом:
docker compose --env-file .env exec -T clickhouse clickhouse-client --query " SELECT Timestamp, LogAttributes['event.name'] AS event, LogAttributes['chatbot.command'] AS command, LogAttributes['chatbot.reply_status'] AS reply_status, LogAttributes['portal.domain'] AS portal FROM telemetry.otel_logs WHERE LogAttributes['portal.domain'] = 'b24-w9apyi.bitrix24.ru' ORDER BY Timestamp DESC LIMIT 30 " --format=PrettyCompact
Результат будет выглядеть примерно так:

Что дальше
На проекте стартера можно собрать почти любое приложение-кастомизацию для своего портала, поэтому в следующий раз мы попробуем сделать и подключить ещё что-то интересное и полезное, или продолжим тему мониторинга, продуктовых событий и алертов.
Если у вас есть вопросы или запросы на освещение других тем — пишите в комментариях, а мы постараемся рассказать про них в следующих материалах.
Содержание цикла статей про создание приложений с AI-агентами
Пишем первое приложение с AI-стартером, чтобы видеть прибыли и убытки
Добавляем в бизнес-портал Битрикс24 роботов для автоматизации
Что даёт воспроизводимая среда разработки и как развернуть контейнеры на VPS
Анализ и модернизация коннектора баз данных с помощью AI-агентов
OTel Collector вкастомизации Битрикс24: подключаем Observability
Технический и продуктовый мониторинг за кастомизациями Битрикс24: как настроить и на что смотреть