Все уроки
DevOps и облакоПродвинутый12 мин чтенияПолезно знать

OpenTelemetry

Один открытый стандарт для трейсов, метрик и логов, чтобы проследить запрос через все сервисы.

  • Traces
  • Spans
  • Collector
  • Context propagation
Шаг 1/6
Checkout длится 8 секунд, пользователи в ярости. Пора звать детективов: metrics, traces и logs.

За 30 секунд

OpenTelemetry (OTel) — open-source-стандарт сбора телеметрии, не привязанный к вендору: трейсы (путь одного запроса через систему), метрики (числа во времени) и логи. Вы один раз инструментируете код с помощью OTel SDK, отправляете данные в формате OTLP в Collector, а оттуда экспортируете в любой бэкенд: Grafana, Jaeger, Datadog, Honeycomb, New Relic, Elastic или облачного провайдера. Это проект CNCF, который поддерживают почти все observability-вендоры.

Не в моём ежедневном стеке — я понимаю принципы и изучу детали, когда это понадобится проекту.

Почему это важно

  • В системе из нескольких сервисов, очередей и баз одни логи не скажут, почему конкретный запрос был медленным; распределённый трейс показывает каждый переход и его время.
  • Он избавляет от vendor lock-in: инструментация пишется один раз под открытый API, а смена бэкенда — это изменение конфигурации Collector, а не переписывание кода.
  • Автоинструментация HTTP, баз данных, очередей и фреймворков даёт полезные трейсы за минуты, ещё до того, как вы напишете хоть один свой span.
  • Команды приходят к нему, когда перерастают простые проверки доступности и error tracking и им нужно ответить на вопрос «куда уходит время?» в масштабе нескольких сервисов.

Как это работает

01Три сигнала и как они связаны

Телеметрия бывает трёх основных видов (сигналов). Трейс — это история одного запроса: дерево спанов, где каждый span — одна единица работы (HTTP-обработчик, SQL-запрос, вызов платёжного API) со временем начала, длительностью, статусом и атрибутами вроде http.route или db.system. Метрики — числовые измерения, агрегированные во времени: частота запросов, доля ошибок, гистограммы задержек, глубина очереди, память. Их дёшево хранить, и они отлично подходят для дашбордов и алертов. Логи — записи о событиях с временными метками. Сила OpenTelemetry — в корреляции: логи, записанные внутри спана, несут его trace ID и span ID, а метрики могут нести exemplars со ссылками на примерные трейсы. Так алерт по p95-задержке приводит вас к медленному трейсу, а трейс — к точным строкам логов. Трейсы, метрики и логи в спецификации стабильны; профилирование добавляется как четвёртый сигнал.

  • Трейс = дерево спанов для одного запроса.
  • Метрики = дешёвые агрегаты для алертов и дашбордов.
  • Логи = события, связанные через trace ID и span ID.

02Архитектура: API, SDK, экспортёры и Collector

OTel чётко разделяет обязанности. API — это то, что вызывают библиотеки и ваш код, чтобы создавать спаны и записывать метрики; сам по себе он ничего не делает, поэтому библиотеки могут безопасно от него зависеть. SDK — реализация, которую вы настраиваете в приложении: он решает, что сэмплировать, собирает данные в батчи и передаёт экспортёрам. Экспортёры отправляют данные наружу, обычно по OTLP (OpenTelemetry Protocol), на порт 4317 для gRPC или 4318 для HTTP. Библиотеки инструментации патчат популярные фреймворки и клиенты (Express, Fastify, Next.js, Laravel, pg, mysql, Redis, HTTP-клиенты), чтобы создавать спаны автоматически. Collector — отдельный сервис, который принимает телеметрию, обрабатывает её (батчинг, фильтрация, удаление чувствительных атрибутов, сэмплирование) и экспортирует в один или несколько бэкендов. Его можно запускать как агент рядом с каждым приложением, как центральный шлюз или и так и так. Ресурсы описывают, кто отправил данные: service.name, service.version, deployment.environment.name.

03Инструментация сервиса на Node.js

Быстрее всего начать в Node.js с SDK и набора автоинструментаций. Установите @opentelemetry/sdk-node, @opentelemetry/auto-instrumentations-node и OTLP-экспортёр, затем подгрузите небольшой файл настройки до кода приложения — для ES-модулей через node --import. Настраивайте через стандартные переменные окружения, а не захардкоженные значения, чтобы один и тот же образ работал во всех окружениях: OTEL_SERVICE_NAME, OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_RESOURCE_ATTRIBUTES и OTEL_TRACES_SAMPLER. В Next.js есть встроенный хук instrumentation.ts и хелпер @vercel/otel. Laravel и PHP используют расширение OpenTelemetry для PHP плюс пакеты автоинструментации. Начните с автоинструментации, посмотрите на реальные трейсы, а затем добавляйте свои спаны только вокруг важных бизнес-шагов.

js
// instrumentation.mjs: run with  node --import ./instrumentation.mjs server.mjs
import { NodeSDK } from "@opentelemetry/sdk-node";
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
 
// Endpoint and service name come from OTEL_EXPORTER_OTLP_ENDPOINT / OTEL_SERVICE_NAME
const sdk = new NodeSDK({
  traceExporter: new OTLPTraceExporter(),
  metricReader: new PeriodicExportingMetricReader({ exporter: new OTLPMetricExporter() }),
  instrumentations: [getNodeAutoInstrumentations({ "@opentelemetry/instrumentation-fs": { enabled: false } })],
});
 
sdk.start();
process.on("SIGTERM", () => sdk.shutdown().finally(() => process.exit(0)));

04Свои спаны и атрибуты

Автоинструментация показывает HTTP-вызовы и обращения к базе, но не вашу бизнес-логику. Добавьте span вокруг значимых шагов вроде «расчёт цены», «резервирование товара» или «генерация PDF-счёта» и прикрепите атрибуты, которые помогут потом искать: order.items_count, tenant.tier, cache.hit. Для распространённых имён следуйте semantic conventions (http.request.method, db.system.name, messaging.system), чтобы бэкенды их понимали. Помните о кардинальности атрибутов: ID заказа на спане — нормально, ведь span — это одно событие, но никогда не кладите ID пользователей или URL с ID в атрибуты метрик, потому что каждое уникальное значение создаёт новый временной ряд. Записывайте исключения в span и выставляйте статус ошибки, чтобы неудачные запросы легко находились. Никогда не кладите в атрибуты пароли, токены или полные номера карт.

ts
import { trace, SpanStatusCode } from "@opentelemetry/api";
 
const tracer = trace.getTracer("checkout");
 
export async function placeOrder(cart: { items: unknown[] }) {
  return tracer.startActiveSpan("checkout.place_order", async (span) => {
    span.setAttribute("order.items_count", cart.items.length);
    try {
      const order = await saveOrder(cart); // pg / HTTP calls become child spans
      span.setAttribute("order.id", order.id);
      return order;
    } catch (err) {
      span.recordException(err as Error);
      span.setStatus({ code: SpanStatusCode.ERROR });
      throw err;
    } finally {
      span.end();
    }
  });
}

05Запуск Collector

Отправлять телеметрию из приложений прямо вендору можно, но Collector посередине даёт контроль. Приложения быстро отправляют OTLP в локальный Collector и возвращаются к работе; Collector собирает батчи, повторяет отправку и буферизует, если бэкенд тормозит, так что приложение не страдает. Это правильное место, чтобы добавить атрибуты окружения, отбросить шумные спаны вроде health checks, удалить чувствительные данные и применить tail sampling. Он также умеет собирать метрики Prometheus и читать файлы логов, становясь вашим единым конвейером телеметрии. Используйте contrib-дистрибутив (otelcol-contrib) для полного набора процессоров и экспортёров или соберите минимальный с помощью OpenTelemetry Collector Builder. В каждом пайплайне всегда ставьте процессор memory_limiter первым, а batch — последним, и следите за собственными метриками Collector на предмет потерянных данных.

yaml
# otel-collector.yaml
receivers:
  otlp:
    protocols:
      grpc: { endpoint: 0.0.0.0:4317 }
      http: { endpoint: 0.0.0.0:4318 }
processors:
  memory_limiter: { check_interval: 1s, limit_percentage: 80, spike_limit_percentage: 20 }
  attributes/scrub:
    actions: [{ key: http.request.header.authorization, action: delete }]
  batch: {}
exporters:
  otlphttp: { endpoint: https://otlp.example.com }
service:
  pipelines:
    traces: { receivers: [otlp], processors: [memory_limiter, attributes/scrub, batch], exporters: [otlphttp] }
    metrics: { receivers: [otlp], processors: [memory_limiter, batch], exporters: [otlphttp] }

Когда применять

  • У вас несколько сервисов, очередей или фоновых воркеров, и нужно проследить запрос через них.
  • Вы хотите избежать привязки к одному observability-вендору или планируете позже сменить бэкенд.
  • Для отладки задержек и ошибок в продакшене нужны связанные между собой трейсы, метрики и логи.
  • Ваша платформа или фреймворк уже нативно поддерживает OTel (Next.js, Kubernetes, облачные балансировщики, многие базы данных).

Когда не стоит

  • Один небольшой монолит, где error tracking, проверки доступности и хорошие логи уже отвечают на все вопросы.
  • У команды нет времени поддерживать tracing-бэкенд или платить за него; телеметрия, на которую никто не смотрит, — это только расходы.
  • Когда встроенный агент hosted-инструмента уже покрывает ваши потребности, а переносимость вам не важна.

Альтернативы

  • Агенты вендоров (Datadog, New Relic)

    Более быстрая настройка «всё в одном» с отполированным UI, но проприетарная инструментация и счета по использованию. Большинство теперь тоже принимает OTLP.

  • Sentry

    В первую очередь error tracking, плюс трассировка; его JavaScript SDK внутри построен на OpenTelemetry.

  • Только Prometheus + логи

    Метрики и логи без трейсов: проще и дешевле, но проследить один запрос через сервисы нельзя.

  • Облачные инструменты

    AWS X-Ray, Google Cloud Trace, Azure Monitor; все они теперь принимают данные OpenTelemetry.

Лучшие практики

  1. Задавайте service.name, service.version и deployment.environment.name для каждого сервиса.
  2. Настраивайте через переменные окружения OTEL_*, чтобы код был одинаковым во всех окружениях.
  3. Начните с автоинструментации, затем добавьте несколько своих спанов вокруг критичных для бизнеса шагов.
  4. Отправляйте данные через Collector с процессорами memory_limiter и batch.
  5. Сэмплируйте трейсы осознанно: head sampling — для объёма, tail sampling — чтобы сохранить ошибки и медленные запросы.
  6. Встраивайте trace ID в логи, чтобы переходить от строки лога к её трейсу.
  7. Держите кардинальность атрибутов метрик низкой и никогда не записывайте в атрибуты секреты или персональные данные.

Частые ошибки

  • Подгружать инструментацию после того, как приложение уже импортировало свои библиотеки, — тогда ничего не патчится.
  • Забыть service.name, из-за чего каждый сервис отображается как unknown_service.
  • Класть ID пользователей или полные URL в метки метрик и взрывать число временных рядов.
  • Ломать распространение контекста через очереди или самописные HTTP-клиенты, получая разорванные трейсы.
  • Хранить 100% трейсов в продакшене и получать огромный счёт за хранилище.

Глубже

06Tail sampling в масштабе

Tail sampling сохраняет трейсы целиком в зависимости от того, что в них произошло, но спаны одного трейса приходят из многих сервисов в разное время. Collector держит спаны в памяти в течение периода ожидания решения (часто 10–30 секунд), а затем применяет политики: status_code ERROR, задержка выше порога, конкретные атрибуты или вероятностная доля для остальных. При нескольких репликах Collector все спаны одного трейса должны попасть в один инстанс, поэтому запускают два уровня: агенты или первый шлюзовой слой с load-balancing-экспортёром по ключу trace ID, а за ними — слой сэмплирования. Рассчитывайте память как трафик × время ожидания и следите за метриками процессора по отброшенным трейсам. Помните, что отброшенные при сэмплировании трейсы влияют и на метрики, построенные из спанов; генерируйте RED-метрики (rate, errors, duration) коннектором spanmetrics до сэмплирования, чтобы дашборды оставались точными.

yaml
processors:
  tail_sampling:
    decision_wait: 15s
    num_traces: 100000
    policies:
      - { name: errors, type: status_code, status_code: { status_codes: [ERROR] } }
      - { name: slow, type: latency, latency: { threshold_ms: 1000 } }
      - { name: baseline, type: probabilistic, probabilistic: { sampling_percentage: 5 } }

07Распространение контекста через очереди и асинхронную работу

Автоматическое распространение покрывает синхронные HTTP и gRPC. С асинхронной работой нужна аккуратность. Когда вы публикуете задачу в Redis, RabbitMQ или Kafka, встраивайте активный контекст в заголовки сообщения; когда воркер её забирает, извлекайте контекст и начинайте consumer-span. Semantic conventions моделируют это через виды спанов producer и consumer. Сильно отложенная задача ставит вопрос дизайна: должны ли её спаны быть дочерними к запросу, который её создал? Для коротких конвейеров — да, но задача, выполняемая через час, даёт странный, очень длинный трейс. Альтернатива — новый трейс со span link на исходный контекст: связь сохраняется, а трейс не растягивается. Пакетные потребители должны использовать ссылки на множество контекстов producer-ов. Baggage может переносить между сервисами значения вроде tenant ID, но он отправляется с каждым запросом, поэтому держите его маленьким и никогда не кладите туда секреты.

ts
import { context, propagation, trace } from "@opentelemetry/api";
 
// Producer: carry the trace context inside the job payload
export function withTraceHeaders<T extends object>(payload: T) {
  const headers: Record<string, string> = {};
  propagation.inject(context.active(), headers);
  return { ...payload, otel: headers };
}
 
// Consumer: continue the trace when the job runs
export function runJob(job: { otel?: Record<string, string> }, work: () => Promise<void>) {
  const parent = propagation.extract(context.active(), job.otel ?? {});
  return context.with(parent, () =>
    trace.getTracer("worker").startActiveSpan("job.process", (span) => work().finally(() => span.end())),
  );
}

08Дизайн метрик: инструменты, temporality и кардинальность

Метрики OTel используют инструменты: Counter для того, что только растёт (запросы, отправленные байты), UpDownCounter для значений, которые растут и падают (размер очереди, активные соединения), Histogram для распределений (задержка, размер payload) и Gauge для снимков текущих значений. Именно гистограммы дают p95 и p99; экспоненциальные гистограммы обеспечивают хорошую точность при меньшем числе бакетов. Temporality важна при экспорте: бэкенды в стиле Prometheus ожидают кумулятивные значения, а некоторые вендоры предпочитают delta, и экспортёр можно настроить в любую сторону. Кардинальность — главный драйвер стоимости: каждая уникальная комбинация значений атрибутов — отдельный временной ряд. http.route=/orders/:id — нормально; «сырой» URL /orders/123 — катастрофа. Используйте views, чтобы отбрасывать или переименовывать атрибуты и менять бакеты гистограмм, не трогая код инструментации.

09Накладные расходы, безопасность и поэтапное внедрение

Инструментация не бесплатна, но обычно её цена мала: несколько процентов CPU, в основном на создание и экспорт спанов. Держите её низкой с помощью batch span processor (никогда не simple processor в продакшене), сэмплирования и отключения шумных инструментаций вроде спанов файловой системы или DNS. Экспорт никогда не должен блокировать запросы: если бэкенд недоступен, SDK отбрасывает данные после заполнения очереди, а не замедляет приложение. Очищайте чувствительные данные на двух уровнях: не собирайте их в коде и удаляйте заголовки, query-строки и SQL-параметры в Collector. Внедряйте посервисно, начиная с edge и самого критичного пути, и сравнивайте задержку до и после. Относитесь к Collector как к продакшен-инфраструктуре: health checks, лимиты ресурсов, горизонтальное масштабирование и алерты на потерянные спаны.

Реальные ситуации

01

Трейсы обрываются на очереди

Проблема

Трейсы показывают API-запрос, который ставит задачу в очередь, но спаны воркера появляются как отдельные трейсы, и никто не видит полного пути заказа.

Как решить

Контекст не передаётся через очередь. Проверьте, есть ли инструментация для вашей библиотеки очередей; включите её и в producer, и в consumer. Если нет — вручную встройте контекст в payload задачи через propagation.inject при постановке в очередь, а в воркере извлеките его и запустите обработчик внутри context.with. Выберите между parent-child и ссылками: для задач, выполняемых за секунды, parent-child даёт один читаемый трейс; для отложенных или пакетных задач начинайте новый трейс со span link. Добавьте атрибуты messaging.system и messaging.destination.name. Проверьте в бэкенде, что producer-span и consumer-span теперь имеют общий trace ID или связь.

02

Счёт за трассировку растёт быстрее трафика

Проблема

После инструментации всех сервисов с настройками по умолчанию счёт observability-вендора удвоился. Большинство сохранённых трейсов — health checks и быстрые успешные запросы.

Как решить

Добавьте шлюзовой Collector, если его нет. Отбрасывайте спаны health check и readiness probe filter-процессором. Добавьте tail sampling: сохраняйте 100% ошибок и запросов медленнее порога вашего SLO и 2–10% всего остального. Генерируйте RED-метрики коннектором spanmetrics до сэмплирования, чтобы дашборды и алерты оставались точными. Отключите в SDK малоценные инструментации (fs, dns, net). Проверьте атрибуты на большие значения вроде текста SQL или payload и обрежьте или удалите их. Наконец, задайте бюджет на сервис и посмотрите число спанов по сервисам, чтобы найти самые шумные.

03

Миграция с агента вендора на OpenTelemetry

Проблема

Компания использует проприетарный APM-агент и хочет оценить другой бэкенд, не теряя видимости во время переезда.

Как решить

Разверните Collector с OTLP-ресиверами. Инструментируйте один сервис через OTel SDK и автоинструментации, сохранив прежние имена сервисов. Настройте Collector на экспорт и текущему вендору (большинство принимает OTLP), и новому бэкенду, чтобы сравнивать их на одинаковых данных. Переносите сервисы по одному и удаляйте старый агент, только когда OTel-версия показывает такое же покрытие; никогда не запускайте два агента, патчащих одни и те же библиотеки в одном процессе. Воссоздайте ключевые дашборды и алерты на именах из semantic conventions. После оценки сменить вендора или остаться — это изменение конфигурации Collector, без изменений в коде.

Шпаргалка

Установить Node SDK и автоинструментацию
npm i @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node @opentelemetry/exporter-trace-otlp-http
Старт без кода для Node.js
node --require @opentelemetry/auto-instrumentations-node/register app.js
Задать имя сервиса
OTEL_SERVICE_NAME=checkout-api
Направить в Collector
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318
Добавить атрибуты ресурса
OTEL_RESOURCE_ATTRIBUTES=service.version=1.4.2,deployment.environment.name=production
Сохранять 10% новых трейсов
OTEL_TRACES_SAMPLER=parentbased_traceidratio OTEL_TRACES_SAMPLER_ARG=0.1
Отладка: выводить спаны в консоль
OTEL_TRACES_EXPORTER=console
Запустить локальный Collector
docker run -p 4317:4317 -p 4318:4318 -v ./otel-collector.yaml:/etc/otelcol-contrib/config.yaml otel/opentelemetry-collector-contrib
Локальный UI для трейсов
docker run -p 16686:16686 -p 4318:4318 jaegertracing/jaeger
Формат заголовка W3C trace
traceparent: 00-<32-hex trace-id>-<16-hex span-id>-01

Вопросы и ответы

Q1Чем трейс отличается от спана?

Span — одна операция с замеренным временем: входящий HTTP-запрос, запрос к базе, вызов другого сервиса или фрагмент бизнес-логики. У него есть имя, время начала и конца, статус, атрибуты и родитель. Трейс — всё дерево спанов с общим trace ID, отражающее путь одного запроса через систему. Корневой span — обычно первая точка входа, например запрос на API-шлюзе, а дочерние спаны показывают работу, которую он вызвал. Глядя на водопад трейса, видно, куда ушло время и где произошла ошибка, — отдельные логи этого показать не могут.

Q2Что такое распространение контекста (context propagation)?

Context propagation — это то, как трейс продолжается через границы процессов. Когда сервис A вызывает сервис B, SDK сервиса A встраивает текущие trace ID и span ID в исходящий запрос — по умолчанию в виде заголовка W3C traceparent, плюс tracestate и опционально baggage. Инструментация сервиса B извлекает их и создаёт свои спаны дочерними к спану A, так что оба оказываются в одном трейсе. Для HTTP и gRPC это происходит автоматически. Для очередей сообщений нужно передавать контекст в заголовках или атрибутах сообщения и извлекать его в потребителе — многие инструментации для messaging так и делают. Если распространение ломается, вы получаете отдельные, не связанные трейсы.

Q3Зачем Collector, если можно экспортировать прямо из приложения?

Collector отвязывает приложение от бэкенда. Приложение отправляет данные на ближайший эндпоинт и идёт дальше, а Collector занимается батчингом, повторами, сжатием и буферизацией, когда бэкенд медленный. Он централизует обработку: добавление атрибутов, фильтрацию health checks, удаление чувствительных данных и tail sampling, которому нужно видеть трейсы целиком и который невозможно сделать внутри одного сервиса. Он также позволяет менять или добавлять бэкенды без передеплоя приложений и во время миграции отправлять одни и те же данные двум вендорам. Прямой экспорт подходит для быстрого старта или одного небольшого сервиса, но в продакшен-системах обычно есть Collector.

Q4Что такое сэмплирование и какое бы вы использовали?

Сэмплирование решает, какие трейсы сохранять, потому что хранить каждый трейс при высоком трафике дорого. Head sampling решает в начале трейса, например сохраняет 10% с parent-based-сэмплером TraceIdRatio. Это дёшево и согласованно между сервисами, но может отбросить ту самую редкую ошибку, которая вам важна. Tail sampling решает после завершения трейса, в Collector, поэтому может сохранить все ошибки, все медленные запросы и небольшой процент остального. Для этого все спаны трейса должны попасть в один и тот же инстанс Collector, и он расходует память. Распространённая комбинация — лёгкий head sampling в SDK плюс политики tail sampling в шлюзовом Collector.

Q5Чего OpenTelemetry не делает?

OpenTelemetry собирает и передаёт телеметрию; он её не хранит, не визуализирует и не алертит. Вам всё равно нужен бэкенд: Grafana с Tempo, Prometheus или Mimir и Loki, Jaeger для трейсов или коммерческий сервис вроде Honeycomb, Datadog или New Relic. Он также не решает, что важно: имена сервисов, атрибуты, сэмплирование и то, какие бизнес-операции заслуживают спанов, выбираете вы. И он не заменяет error tracking с группировкой, отслеживанием релизов и пользовательским контекстом — поэтому многие команды используют его вместе с Sentry. Думайте о нём как о стандартной «сантехнике», которая делает остальной observability-стек взаимозаменяемым.

Вопросы уровня senior

S1Как внедрить OpenTelemetry в 40 сервисов, которыми владеют разные команды?

Относитесь к этому как к платформенному продукту. Дайте для каждого языка тонкий внутренний пакет или базовый образ, который настраивает SDK, атрибуты ресурса, propagators и экспортёры из переменных OTEL_*, чтобы командам нужно было добавить одну зависимость, а не проектировать настройку. Запускайте Collector как агенты (DaemonSet или sidecar) плюс шлюзовой слой, которым владеет платформенная команда, — для сэмплирования, очистки и маршрутизации. Опубликуйте соглашения: именование сервисов, обязательные атрибуты вроде tenant tier, какие бизнес-операции получают спаны и правила кардинальности для метрик. Начните с пути запроса от edge до базы, чтобы трейсы сразу были полезны, затем расширяйте. Измеряйте внедрение покрытием спанами по сервисам. Компромисс — централизованный контроль против автономии команд; обычно выигрывает «мощёная дорога» с разумными значениями по умолчанию.

S2Head sampling, tail sampling или без сэмплирования: как решить?

Отталкивайтесь от вопросов и бюджета. Если трафик низкий, храните всё: это проще всего, и вы никогда не теряете данные. На больших объёмах head sampling с parent-based ratio-сэмплером дёшев, не требует памяти Collector и сохраняет трейсы целыми между сервисами, но он «слепой»: редкие ошибки отбрасываются с той же частотой, что и успешные запросы. Tail sampling сохраняет важное (ошибки, медленные запросы, конкретных тенантов) и отбрасывает скучный трафик, но требует stateful-слоя Collector с балансировкой по trace ID, памяти под окно решения и добавляет задержку перед появлением данных. Многие команды комбинируют: щедрый head sampling для ограничения объёма от очень горячих сервисов, tail sampling для умного отбора и метрики из спанов, вычисляемые до сэмплирования, чтобы агрегированные цифры оставались верными.

S3Как связать трейсы OpenTelemetry с логами в уже существующей системе логирования?

Цель — чтобы каждая строка лога, записанная внутри запроса, содержала trace_id и span_id. В Node.js пакеты инструментации для pino и winston автоматически встраивают их в записи логов; в PHP, Java и Python есть похожие интеграции или OTel logs bridge. Оставьте свой текущий логгер: API логов OTel рассчитан на мост из привычных библиотек, а не на прямой вызов. Затем либо отправляйте логи через Collector по OTLP, либо продолжайте отправлять их JSON-файлами и убедитесь, что поле trace_id проиндексировано. В бэкенде настройте переход из просмотра трейса к запросу логов с фильтром по trace_id и из строк логов — к трейсам. Компромисс — объём: отправка всех логов через OTLP централизует конвейер, но увеличивает нагрузку на Collector, поэтому фильтруйте debug-логи как можно раньше.

Глоссарий

Span
Одна операция с замеренным временем внутри трейса: имя, длительность, статус, атрибуты и родительский span.
Трейс (Trace)
Все спаны с общим trace ID, показывающие путь одного запроса через систему.
OTLP
OpenTelemetry Protocol для отправки трейсов, метрик и логов по gRPC (порт 4317) или HTTP (порт 4318).
Collector
Отдельный сервис, который принимает, обрабатывает и экспортирует телеметрию в один или несколько бэкендов.
Ресурс (Resource)
Атрибуты, описывающие источник телеметрии: service.name, версия и окружение.
Semantic conventions
Стандартные имена атрибутов (http.request.method, db.system.name), чтобы инструменты понимали телеметрию из любого источника.
Context propagation
Передача trace ID и span ID между сервисами, обычно в заголовке W3C traceparent.
Tail sampling
Выбор, какие трейсы сохранить, после их завершения — например, все ошибки и медленные запросы.
Кардинальность (Cardinality)
Число уникальных комбинаций значений атрибутов в метрике; высокая кардинальность умножает стоимость хранения.

Есть идея продукта?Давайте создадим его.

Расскажите о своей идее, продукте или команде. Обычно отвечаю в течение одного рабочего дня.