Трассировка OpenTelemetry

Трассировка показывает, что происходило внутри каждого запроса: какой эндпоинт его принял, какие запросы к базе он сделал, какие сервисы и внешние API вызвал и где потратил время. Наблюдатель принимает трассы стандартного OpenTelemetry SDK — отдельная библиотека Наблюдателя не нужна, достаточно OTel SDK для вашего языка и нескольких переменных окружения.

Трассы отправляются на агента стека внутри вашей сети. Агент обезличивает персональные данные и пересылает трассы в Наблюдатель. По ним строятся вкладка «Трассировка», карта сервисов и медленные запросы к БД, а у ошибок приложения появляется ссылка на трассу.

Что нужно для трассировки OpenTelemetry

  • Агент стека — в Docker Compose или Kubernetes. Агент сервера трассы не принимает.
  • В окружении агента NABL_OTLP_ENABLED: "true". В готовом сервисе агента, который выдаёт Наблюдатель при создании стека, это уже включено.
  • Приложение видит агента по сети: в Docker Compose — по имени сервиса nabl-agent в той же сети, в Kubernetes — сервис nabl-agent в том же namespace.

Агент принимает трассы на двух портах внутри сети стека:

Порт Протокол
4318 OTLP/HTTP, protobuf
4317 OTLP gRPC

Важно. OTLP/HTTP в формате JSON агент не принимает — приложение получит ответ 400 bad protobuf. Нужен http/protobuf или gRPC.

Публиковать эти порты наружу не нужно. Если приложение запущено не в Docker, а прямо на вашем компьютере, используйте http://127.0.0.1:4318 и опубликуйте порты агента на localhost — см. локальную разработку.

Как подключить: переменные окружения

Эти переменные понимает OTel SDK любого языка. Пример для сервиса в docker-compose.yml:

yaml
environment:
  OTEL_SERVICE_NAME: api                      # имя сервиса в трассах; без него — unknown_service
  OTEL_EXPORTER_OTLP_ENDPOINT: http://nabl-agent:4318
  OTEL_EXPORTER_OTLP_PROTOCOL: http/protobuf
  OTEL_TRACES_EXPORTER: otlp
  OTEL_METRICS_EXPORTER: none                 # агент принимает только трассы
  OTEL_LOGS_EXPORTER: none
  OTEL_RESOURCE_ATTRIBUTES: deployment.environment=production,service.version=1.4.0
  • OTEL_EXPORTER_OTLP_ENDPOINT — базовый адрес, SDK сам добавит /v1/traces. Если задаёте OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, указывайте полный путь: http://nabl-agent:4318/v1/traces.
  • Для gRPC: OTEL_EXPORTER_OTLP_ENDPOINT=http://nabl-agent:4317 и OTEL_EXPORTER_OTLP_PROTOCOL=grpc.
  • service.version в OTEL_RESOURCE_ATTRIBUTES помогает отличить трассы до и после релиза.
  • Сэмплер SDK оставьте по умолчанию и не задавайте OTEL_TRACES_SAMPLER. Наблюдатель сохраняет все трассы без выборки, поэтому число запросов, доля ошибок и время ответа считаются по всем запросам. Сэмплер в приложении занизит цифры.

Примеры подключения по языкам

Node.js

bash
npm i @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node @opentelemetry/exporter-trace-otlp-proto

Без изменения кода — автоинструментация при запуске (переменные окружения из раздела выше):

bash
node --require @opentelemetry/auto-instrumentations-node/register server.js
# или NODE_OPTIONS="--require @opentelemetry/auto-instrumentations-node/register"

С кодом — файл tracing.js, который подключается до остального кода: node -r ./tracing.js server.js.

js
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-proto'); // proto, не exporter-trace-otlp-http (JSON)

new NodeSDK({
  traceExporter: new OTLPTraceExporter(), // адрес из OTEL_EXPORTER_OTLP_ENDPOINT
  instrumentations: [getNodeAutoInstrumentations({ '@opentelemetry/instrumentation-fs': { enabled: false } })],
}).start();

Python

bash
pip install opentelemetry-distro opentelemetry-exporter-otlp-proto-http
opentelemetry-bootstrap -a install          # инструментации для найденных библиотек: Django, Flask, FastAPI, requests, psycopg…
opentelemetry-instrument python app.py      # или: opentelemetry-instrument gunicorn app:app

Для gunicorn с несколькими воркерами запускайте его через opentelemetry-instrument, как в примере.

Go

go
import (
	"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
	"go.opentelemetry.io/otel/propagation"
	sdktrace "go.opentelemetry.io/otel/sdk/trace"
)

func initTracing(ctx context.Context) (func(context.Context) error, error) {
	exp, err := otlptracehttp.New(ctx) // OTEL_EXPORTER_OTLP_ENDPOINT, protobuf по умолчанию
	if err != nil {
		return nil, err
	}
	tp := sdktrace.NewTracerProvider(sdktrace.WithBatcher(exp)) // имя сервиса из OTEL_SERVICE_NAME
	otel.SetTracerProvider(tp)
	otel.SetTextMapPropagator(propagation.TraceContext{})
	return tp.Shutdown, nil
}

// HTTP-сервер: http.ListenAndServe(":8080", otelhttp.NewHandler(mux, "api"))
// Gin: r.Use(otelgin.Middleware("api"))
// GORM: db.Use(tracing.NewPlugin()), запросы через db.WithContext(ctx)

Java (Spring и другие)

Java-агент OpenTelemetry подключается без изменения кода:

bash
java -javaagent:/otel/opentelemetry-javaagent.jar -jar app.jar

С переменными окружения из раздела выше. В javaagent 2.x протокол http/protobuf используется по умолчанию.

.NET

Пакеты OpenTelemetry.Extensions.Hosting, OpenTelemetry.Instrumentation.AspNetCore и OpenTelemetry.Exporter.OpenTelemetryProtocol:

csharp
builder.Services.AddOpenTelemetry()
    .WithTracing(t => t
        .AddAspNetCoreInstrumentation()
        .AddHttpClientInstrumentation()
        .AddOtlpExporter(o =>
        {
            o.Protocol = OtlpExportProtocol.HttpProtobuf;
            o.Endpoint = new Uri("http://nabl-agent:4318/v1/traces");
        }));

PHP

Расширение OpenTelemetry, SDK с OTLP-экспортёром и автоинструментация вашего фреймворка (пример для Laravel):

bash
pecl install opentelemetry
composer require open-telemetry/sdk open-telemetry/exporter-otlp php-http/guzzle7-adapter \
  open-telemetry/opentelemetry-auto-laravel

К переменным окружения из раздела выше добавьте OTEL_PHP_AUTOLOAD_ENABLED=true.

Как проверить, что трассы приходят

  1. Сделайте несколько запросов к приложению.
  2. Через 5–10 секунд откройте стек → вкладка «Трассировка». Пока трасс нет, там показана короткая инструкция подключения.

Если трассы не появились:

Что видно Причина
в логах приложения 400 или bad protobuf экспортёр шлёт JSON — нужен http/protobuf (в Node.js — пакет exporter-trace-otlp-proto)
ECONNREFUSED / connection refused к nabl-agent приложение не в той же сети, что агент, другое имя сервиса агента или NABL_OTLP_ENABLED не true
404 на /v1/metrics или /v1/logs (агент до 0.9.1) включены экспортёры метрик или логов — задайте OTEL_METRICS_EXPORTER=none и OTEL_LOGS_EXPORTER=none
трассы есть, но сервис называется unknown_service не задан OTEL_SERVICE_NAME
SDK отправляет, а в Наблюдателе пусто агент привязан к серверу, а не к стеку

Больше вариантов — в разделе решение проблем.

Вкладка «Трассировка»

Вкладка открывается на странице стека. Наверху — общая панель: выбор сервиса и период «Данные за» — 1 ч, 6 ч, 24 ч или 7 дн. Сервис и период действуют на всё ниже: карту, обзор, операции и список трасс. Когда выбран сервис, рядом появляется ссылка «Страница сервиса →» — подробнее в статье карта сервисов и страница сервиса.

Карта сервисов

Кто кого вызывает: сервисы, базы данных, брокеры сообщений и внешние адреса. Клик по сервису или базе показывает их трассы в списке ниже. Как читать карту — в отдельной статье.

Обзор

Четыре плитки с небольшими графиками:

  • Входящие запросы — сколько запросов в секунду или минуту и сколько всего за период;
  • Время ответа, p95 — 95% запросов быстрее этого значения;
  • Ошибки — доля и число запросов с ошибкой;
  • База данных — частота запросов к базе и их p95.

У каждой плитки — изменение к прошлому периоду: к прошлому часу, 6 часам, суткам или неделе. Под плитками — одна–три строки о главном: где ошибки, выросло ли время ответа, какая операция самая медленная и что больше всего нагружает базу. Клик по строке открывает эту операцию.

Операции

Таблица операций: HTTP-маршруты, запросы к базе, сообщения очередей и внутренние операции. Фильтр по типу — «Все», «Запросы», «База данных», «Очереди», «Внутренние», поиск — по строке вроде orders, SELECT или /api. Колонки «Вызовов», «p95» и «Ошибки» сортируются, в колонке «Динамика» — график времени ответа с красными точками на ошибках.

Сначала видны пять самых частых операций, остальные — по кнопке «Показать все». Названия читаемые: вместо postgresql:PG.QUERY:SET — «PostgreSQL · SET», а исходящие запросы к внешним сервисам подписаны адресом — «POST api.partner.ru/v1/orders», поэтому разные адреса не сливаются в одну строку. Клик по строке раскрывает графики операции за период — время ответа, вызовы, ошибки — и кнопку «Показать трассы этой операции».

Операции строятся по атрибуту http.route (/users/:id). Если инструментация его не пишет, адрес нормализуется автоматически, но хуже.

Список трасс

Ниже — трассы за выбранный период: операция, число спанов, длительность, время. Фильтры «Все», «OK», «Ошибки» и поиск «Поиск операции / trace id» — по названию любой операции внутри трассы или по идентификатору трассы.

Клик по трассе раскрывает временную шкалу спанов. Клик по спану показывает детали: текст SQL-запроса, атрибуты по разделам, события (например, записанное исключение), время от начала трассы и долю от общей длительности. Кнопка «JSON» у раскрытой трассы выгружает её целиком — со всеми спанами и атрибутами.

Под шкалой спанов — блок «Записи потоков с этим trace_id»: что пришло в запросе и что ответили, если приложение пишет в потоки событий trace_id текущего запроса.

Свёртка служебных спанов

Автоинструментация создаёт на каждый запрос десятки коротких служебных спанов: хуки fastify, middleware express, guards NestJS, получение соединения из пула. Подряд идущие такие спаны сворачиваются в одну строку, например «15 служебных спанов · 0,25 мс», поэтому обработчик, вызовы gRPC и SQL видны сразу. Строка раскрывается по клику, а переключатель «Показать все спаны» над трассой показывает всё без свёртки.

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

Что считается ошибкой в трассе

Спан со статусом ERROR или ответом HTTP 5xx считается ошибкой. Обрыв запроса клиентом (context canceled) — нет. Чтобы в трассе было видно, что именно упало, записывайте исключение в спан: recordException в Node.js и Java, span.RecordError в Go.

Чтобы ошибка на вкладке «Ошибки» вела к своей трассе, добавьте trace_id и span_id в запись лога — см. мониторинг ошибок.

Сроки хранения трасс

Данные Срок
Трассы без ошибок 7 дней
Трассы с ошибками 30 дней
Число запросов, ошибки и p95 по сервисам и операциям 3 месяца

Обзор и операции за длинный период считаются по сохранённым агрегатам, то есть по всем запросам, а не только по оставшимся трассам. На вкладке доступны периоды до 7 дней; за 30 дней обзор и операции можно попросить у ИИ-ассистента — за этот срок остаются только трассы с ошибками.

Персональные данные в трассах

Агент маскирует персональные данные и секреты до отправки: в атрибутах, SQL-запросах, адресах, сообщениях и стеках исключений. Литералы в SQL (WHERE email = '…') заменяются — в Наблюдатель уходит структура запроса без данных. Подробнее — в статье как работает агент.

Проверки сайтов в трассах

HTTP-проверки Наблюдателя приходят с заголовком traceparent и меткой в адресе (?nabl_check=…). Так Наблюдатель находит, какой сервис принимает проверку, и показывает узел проверки на карте. В обзор, операции и список трасс проверки не входят и нагрузку не искажают. Настраивать ничего не нужно; если приложение отвечает на лишний параметр ошибкой, метка в адресе выключится сама. Подробнее — в статье о проверках сайтов.

Что дальше

Попробуйте Наблюдатель

Первый сервер или стек подключается за пару минут. Бесплатно в раннем доступе.

Создать аккаунт

Нашли неточность или не хватает ответа? Напишите в поддержку