Логам конец: как находят ошибки через OpenTelemetry
Логам конец: как находят ошибки через OpenTelemetry
Ошибка прошла через пять сервисов, а в логах — десятки несвязанных сообщений. Вместо этого сервис OpenTelemetry объединяет запрос в одну трассу и помогает быстрее найти причину сбоя.
Ошибка есть, а причины в логах не видно
Пользователь нажимает кнопку оплаты и получает сообщение об ошибке. Frontend отправил запрос, API создал заказ, платёжный сервис подтвердил операцию, но письмо не пришло, а статус в личном кабинете остался прежним.
В логах каждого сервиса что-то записано. Проблема в другом: непонятно, какие сообщения относятся именно к этому запросу и в какой момент нормальный сценарий пошёл не туда.
Можно искать по времени, идентификатору пользователя или номеру заказа. Иногда этого достаточно. Но часы на разных серверах могут немного отличаться, один сервис не записал нужное поле, а фоновая задача выполнилась через несколько минут. Получается знакомая картина: логов много, информации мало.
OpenTelemetry решает эту проблему не за счёт ещё большего количества сообщений. Он связывает операции разных компонентов в одну историю и показывает путь запроса от начала до конца.

Курс изучения Python
Можете пройти наш бесплатный курс по изучению Python
Логи не стали бесполезными
Заголовок этой статьи немного провокационный. Логи всё ещё нужны. Хорошее сообщение об ошибке часто быстрее любого графика объясняет, что именно произошло: соединение с базой разорвано, токен истёк или внешний API вернул неправильный ответ.
Но отдельная строка отвечает в основном на вопрос «что случилось в этой точке». Она редко показывает, что происходило до неё, сколько времени занял предыдущий шаг и какой сервис вызвал текущую операцию.

В небольшом монолите это не всегда проблема. Открыли один файл, нашли исключение, посмотрели соседние записи. В распределённой системе один пользовательский запрос может пройти через API Gateway, сервис авторизации, каталог, базу, очередь и несколько фоновых обработчиков.
Здесь обычное логирование начинает требовать слишком много ручной работы. Нужна связь между событиями. Именно её добавляет трассировка.
Что такое OpenTelemetry?
OpenTelemetry, или сокращённо OTel, — открытый стандарт и набор инструментов для создания, сбора и передачи телеметрии. Он работает с трассами, метриками и логами, а также передаёт общий контекст между компонентами системы.
Важно понимать границу его ответственности. OpenTelemetry не хранит данные и не предоставляет готовый экран для поиска ошибок. Для хранения и визуализации всё равно нужен observability-бэкенд: коммерческий сервис или собственный набор инструментов.
Задача OTel находится раньше. Он определяет, как приложение создаёт телеметрию, в каком формате её передаёт и как сохраняет связь между сигналами.
За счёт этого приложение меньше зависит от конкретной платформы наблюдаемости. Бэкенд можно поменять, не переписывая всю инструментализацию под очередной закрытый SDK. Полной независимости это не гарантирует, но привязка становится заметно слабее.
Trace показывает путь одного запроса
Основная единица распределённой трассировки — trace. Это полная история одной операции: например, оформление заказа или загрузка страницы.
Trace состоит из отдельных участков — span. Один span может описывать входящий HTTP-запрос, другой — обращение к базе, третий — вызов платёжного API, четвёртый — публикацию сообщения в очередь.
У каждого span есть начало, конец, длительность, статус и набор атрибутов. Дочерние операции знают своего родителя, поэтому в интерфейсе наблюдаемости появляется дерево выполнения:
POST /orders ├── проверка пользователя ├── INSERT INTO orders ├── запрос к payment-service │ └── запрос к внешнему API └── публикация order.created
Если весь запрос занял две секунды, трасса покажет, где именно они были потрачены. Возможно, база ответила за 20 миллисекунд, а внешний API ждал почти всё остальное время.
Лог request completed in 2.1s сообщает о медленной работе. Trace объясняет, какая часть запроса оказалась медленной.
Как запрос не теряется между сервисами?
Чтобы собрать spans разных приложений в одну трассу, каждому запросу назначается trace_id. Когда первый сервис обращается ко второму, он передаёт контекст дальше — обычно через HTTP-заголовок traceparent.
traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
Следующий сервис извлекает идентификатор, создаёт дочерний span и передаёт контекст дальше. Благодаря этому операции, выполненные в разных процессах и даже на разных серверах, остаются частью одного trace.
Вручную копировать заголовок обычно не приходится. Инструментализация популярных HTTP-клиентов, серверных фреймворков и систем очередей делает это автоматически.
Но автоматизация не всесильна. Контекст легко потерять в самописном протоколе, фоновой задаче или очереди, для которой не установлена подходящая интеграция. Если трасса внезапно обрывается и начинается новая, первым делом стоит проверить именно propagation.
Логи становятся полезнее внутри трассы
OpenTelemetry не предлагает отказаться от существующей библиотеки логирования. Его сильная сторона — корреляция. В запись можно добавить текущие trace_id и span_id, после чего лог связывается с конкретным участком запроса.
{ «severity»: «ERROR», «message»: «Payment confirmation timed out», «service.name»: «orders-api», «trace_id»: «4bf92f3577b34da6a3ce929d0e0e4736», «span_id»: «00f067aa0ba902b7» }
Разработчик находит ошибку в логах и открывает соответствующую трассу. Или идёт в обратную сторону: замечает медленный span и смотрит сообщения, созданные во время его выполнения.
Ценность появляется не из-за нового формата лога, а из-за сохранённой связи. Вместо нескольких независимых источников мы получаем разные представления одного события.
Метрики отвечают на другой вопрос
Trace подробно показывает один запрос, но не сообщает общую картину. Чтобы понять, выросло ли время ответа у всех пользователей, нужны метрики.
Метрика может показать количество запросов, долю ошибок, загрузку процессора или распределение времени ответа за определённый период. По ней удобно заметить проблему: после новой версии 95-й процентиль задержки вырос с 300 миллисекунд до двух секунд.
Затем разработчик переходит к traces за этот период и ищет, какие операции стали медленнее. Найдя подозрительный span, открывает связанные логи и читает детали ошибки.
На практике сигналы дополняют друг друга. Метрики показывают масштаб, traces — путь, логи — подробности конкретного события. Если собирать их отдельно и под разными именами, значительная часть пользы теряется.
Автоинструментализация даёт быстрый старт
Для популярных языков и фреймворков OpenTelemetry предлагает готовые библиотеки инструментализации. Они могут автоматически создавать spans для входящих HTTP-запросов, обращений к базе и вызовов через поддерживаемые клиенты.
В Python-приложении базовый запуск может выглядеть так:
pip install opentelemetry-distro opentelemetry-exporter-otlp opentelemetry-bootstrap -a install OTEL_SERVICE_NAME=orders-api \ OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317 opentelemetry-instrument uvicorn main:app
opentelemetry-bootstrap находит установленные поддерживаемые библиотеки и добавляет подходящие пакеты инструментализации. Команда opentelemetry-instrument запускает приложение с настроенным OpenTelemetry без ручного изменения каждого endpoint.
Это хороший способ увидеть первую трассу, но не финальная настройка production-системы. Автоматическая инструментализация знает о технических операциях, однако почти ничего не понимает о бизнес-логике.
Она покажет запрос POST /orders и обращение к PostgreSQL. Но не объяснит, что между ними приложение резервировало товар, применяло промокод и проверяло лимит пользователя.
Важные операции приходится описывать вручную
Ручные spans нужны там, где техническое действие имеет смысл для продукта. Название SELECT говорит мало. Название reserve_inventory уже объясняет, какой этап оформления заказа выполнялся.
from opentelemetry import trace from opentelemetry.trace import Status, StatusCode tracer = trace.get_tracer(__name__) def reserve_inventory(order_id: int, product_id: int): with tracer.start_as_current_span(«reserve_inventory») as span: span.set_attribute(«order.id», order_id) span.set_attribute(«product.id», product_id) try: reserve_product(product_id) except Exception as error: span.record_exception(error) span.set_status(Status(StatusCode.ERROR)) raise
Такой span покажет длительность операции, связанные атрибуты и исключение. Если резервирование вызывается внутри HTTP-запроса, span автоматически становится дочерним для текущей трассы.
Но превращать каждую функцию в отдельный span не нужно. Избыточная детализация увеличивает объём данных и усложняет чтение. Обычно полезны границы между сервисами, запросы к внешним системам и важные этапы бизнес-процесса.
Хорошее правило простое: span стоит добавлять там, где при сбое действительно захочется узнать длительность, результат и контекст операции.

Курс изучения C#
Можете пройти наш бесплатный курс по изучению C#
Атрибуты важнее красивых названий
Без атрибутов трасса быстро превращается в набор одинаковых блоков. Именно атрибуты позволяют отфильтровать запросы по сервису, маршруту, версии приложения, региону или типу операции.
OpenTelemetry использует Semantic Conventions — единые названия для распространённых данных. Например, HTTP-метод записывается как http.request.method, код ответа — как http.response.status_code, а имя сервиса — как service.name.
Общие соглашения особенно полезны, когда систему пишут на разных языках. Java-сервис и Python-сервис должны одинаково называть одно и то же понятие. Иначе фильтры, панели и запросы быстро обрастают исключениями.
Для бизнес-атрибутов придётся создать собственные правила. Лучше заранее договориться между вариантами order.id, order_id и purchase.identifier, чем потом поддерживать все три.
Collector отделяет приложение от хранилища
Приложение может отправлять телеметрию напрямую в observability-бэкенд. Для небольшого проекта этого иногда достаточно. Но по мере роста системы между приложением и хранилищем обычно появляется OpenTelemetry Collector.
Collector принимает данные через receivers, обрабатывает их через processors и отправляет дальше через exporters. В нём можно пакетировать записи, удалять лишние атрибуты, ограничивать память, фильтровать данные и направлять разные сигналы в разные системы.
receivers: otlp: protocols: grpc: http: processors: batch: {} exporters: otlp: endpoint: telemetry-backend:4317 service: pipelines: traces: receivers: [otlp] processors: [batch] exporters: [otlp]
Сам факт добавления раздела в конфигурацию ещё не включает компонент. Receiver, processor и exporter должны быть указаны в соответствующем pipeline внутри service.
Collector удобен ещё и тем, что настройки передачи данных меняются в одном месте. При смене бэкенда не приходится перенастраивать каждый сервис отдельно.
Почему нельзя сохранять каждую трассу?
На небольшом трафике можно собирать все traces. В загруженной системе это быстро становится дорого: один запрос создаёт несколько spans, а миллионы запросов превращаются в огромный поток данных.
Поэтому применяется sampling — сохранение только части трасс. При head sampling решение принимается в начале запроса. Например, система оставляет 5% traces. Такой подход простой и дешёвый, но важная ошибка тоже может попасть в отброшенные 95%.
Tail sampling принимает решение после получения всей или большей части трассы. Так можно сохранить все запросы с ошибками, все слишком медленные операции и небольшую долю обычного трафика.
Звучит идеально, но есть цена. Tail sampler должен некоторое время хранить незавершённые traces, видеть spans разных сервисов и выдерживать большой поток. Его сложнее масштабировать и настраивать.
Sampling нельзя включать по принципу «оставим один процент и забудем». Нужно понимать, какие редкие события система рискует потерять и достаточно ли данных останется для расследования.
Неосторожная телеметрия создаёт новые проблемы
В attributes, logs и baggage легко случайно записать email, токен, текст запроса или данные банковской карты. После этого чувствительная информация отправляется во внешнюю систему, хранится дольше ожидаемого и становится доступна большему количеству сотрудников.
Особенно осторожно нужно работать с HTTP-заголовками и телами запросов. Собирать их целиком «на всякий случай» — плохая идея. Полезнее явно разрешить небольшой набор безопасных полей и удалять всё остальное на уровне приложения или Collector.
Есть и менее очевидная проблема — высокая кардинальность. Если добавить user.id или уникальный order.id в каждую метрику, количество временных рядов может резко вырасти. Для trace отдельный идентификатор бывает полезен, а для metric способен создать серьёзную нагрузку и неожиданный счёт.
Baggage тоже не предназначен для секретов. Его значения передаются дальше между сервисами и потенциально могут покинуть доверенную часть системы.
OpenTelemetry не исправляет плохое приложение
После подключения автоинструментализации интерфейс быстро наполняется цветными полосами. Это создаёт приятное ощущение контроля, но само количество spans ничего не гарантирует.
Если сервисы названы случайно, контекст теряется на очереди, атрибуты не согласованы, а ошибки всегда имеют статус OK, трассировка мало поможет. Получится ещё одно дорогое хранилище данных, в котором сложно искать.
Есть и риск начать инструментировать всё подряд до появления реальной потребности. Для небольшого монолита с понятными структурированными логами полноценный Collector, traces и отдельный backend могут оказаться лишней инфраструктурой.
Я бы не подключал OpenTelemetry только потому, что «так делают большие компании». Сначала должна существовать конкретная проблема: запрос проходит через несколько компонентов, ошибки трудно связать, задержка непредсказуема или текущие инструменты не показывают полную картину.
С чего начать без лишнего усложнения?
Начинать лучше с одного важного пользовательского сценария. Например, с регистрации, оформления заказа или создания отчёта. Нужно проследить его от входящего запроса до последней операции и убедиться, что контекст не теряется.
Автоинструментализация даст HTTP-запросы и обращения к поддерживаемым библиотекам. Затем можно добавить несколько ручных spans вокруг бизнес-этапов, настроить service.name и связать существующие логи с trace_id.
После этого стоит специально вызвать ошибку и проверить весь путь расследования. Видно ли её на метрике? Можно ли открыть проблемный trace? Понятно ли, какой span завершился неудачно? Доступны ли рядом нужные логи?
Если переходы между этими данными работают, инструментализация уже приносит пользу. Остальные сервисы можно подключать постепенно, опираясь на реальные инциденты, а не на желание собрать максимум возможных сигналов.
Логи остаются, но перестают быть одинокими
OpenTelemetry не заменяет логирование. Он помещает логи в контекст, добавляет путь запроса и связывает отдельные события с общей картиной работы системы.
Когда пользователь жалуется на медленную оплату, разработчик больше не ищет совпадения по времени в пяти разных файлах. Он открывает trace, замечает задержку во внешнем вызове и переходит к логам конкретного span.
Именно здесь проходит граница между обычным накоплением данных и наблюдаемостью. Важно не только записать, что система делала. Нужно сохранить связи, которые помогут объяснить, почему она повела себя именно так.
Для небольшого приложения хорошо организованных логов всё ещё может быть достаточно. Но когда один запрос пересекает несколько процессов, очередей и сервисов, OpenTelemetry превращает расследование из археологии в нормальную инженерную работу.
Похожие записи
Оцените материал:
Похожие записи
Нефтяной рынок на перепутье: выход Венесуэлы из ОПЕК, сделка с США и будущее картеля
06.09.2026
ЗАМЕНА УЧИТЕЛЯ НА ИИ РУКАМИ САМОГО УЧИТЕЛЯ: ЗАЧЕМ ШКОЛЬНЫХ ПЕДАГОГОВ МАССОВО ПОДСАЖИВАЮТ НА ЦИФРОВЫЕ ПОМОЩНИКИ ОТ «СБЕРОБРАЗОВАНИЯ»
06.09.2026
OpenAI подтверждает «инцидент с Wiki» и заявляет, что «работает над структурой» для более полной информации.
06.09.2026Присоединяйтесь и подпишитесь на рассылку самых свежих новостей по Email
Получайте свежие новости и идеи на почту. Без спама — только самое интересное.
Нажимая «Подписаться», вы соглашаетесь с политикой конфиденциальности.
