
Схема потока: Приложение → OTLP → OTel Collector → (обработка) → Jaeger/Tempo → графики и поиск → быстрая диагностика.
Ниже — минимальный стенд на локалке: Jaeger для просмотра трейсов и OTel Collector как «шлюз» от приложений.
version: "3.8"
services:
jaeger:
image: jaegertracing/all-in-one:1.50
ports:
- "16686:16686" # UI
- "14250:14250" # gRPC для приёма от коллектора
otel-collector:
image: otel/opentelemetry-collector-contrib:0.91.0
command: ["--config=/etc/otel-collector-config.yaml"]
volumes:
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml:ro
ports:
- "4317:4317" # OTLP gRPC (если решите использовать)
- "4318:4318" # OTLP HTTP
depends_on:
- jaeger
Файл конфигурации коллектора otel-collector-config.yaml:
receivers:
otlp:
protocols:
http:
grpc:
processors:
batch: {}
exporters:
jaeger:
endpoint: jaeger:14250
tls:
insecure: true
logging:
loglevel: info
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [jaeger, logging]
Запуск:
docker compose up -d
Откройте Jaeger: http://localhost:16686
Добавим автотрейсинг и выведем trace_id в логи, чтобы быстро прыгать из лога в нужный трейс.
Установите зависимости:
npm init -y
npm install express pino @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node \
@opentelemetry/exporter-trace-otlp-http @opentelemetry/resources \
@opentelemetry/semantic-conventions @opentelemetry/api
Файл tracing.js:
// tracing.js
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
const { Resource } = require('@opentelemetry/resources');
const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions');
const sdk = new NodeSDK({
resource: new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: 'checkout-api',
[SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]: process.env.NODE_ENV || 'dev',
}),
traceExporter: new OTLPTraceExporter({
url: process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT || 'http://localhost:4318/v1/traces',
}),
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start().then(() => {
// Готово к приёму трафика
}).catch((err) => {
console.error('OTel init error', err);
});
module.exports = sdk;
Файл app.js:
// app.js
require('./tracing');
const express = require('express');
const pino = require('pino');
const https = require('https');
const { context, trace } = require('@opentelemetry/api');
const logger = pino({ level: 'info' });
const app = express();
function httpGet(url) {
return new Promise((resolve, reject) => {
https.get(url, (res) => {
res.on('data', () => {});
res.on('end', () => resolve(res.statusCode));
}).on('error', reject);
});
}
app.get('/checkout', async (req, res) => {
const span = trace.getSpan(context.active());
const traceId = span ? span.spanContext().traceId : undefined;
logger.info({ trace_id: traceId, event: 'start_checkout' });
// Имитируем работу
await new Promise((r) => setTimeout(r, Math.random() * 200 + 50));
// Исходящий вызов (попадёт в тот же трейс благодаря автотрейсингу)
await httpGet('https://httpbin.org/status/200');
logger.info({ trace_id: traceId, event: 'finish_checkout' });
res.json({ ok: true, trace_id: traceId });
});
app.listen(3000, () => {
logger.info('Server started on http://localhost:3000');
});
Запуск приложения:
node app.js
trace_id.checkout-api и найдите трейсы — вы увидите входящий запрос, искусственную «работу» и исходящий вызов.Фокус: теперь любой лог события можно связать с конкретным трейсом. Это экономит минуты и часы на разбор инцидентов.
В реальном трафике полная трассировка каждого запроса редко нужна — это дорого. Золотое правило: собирать всё по ошибкам и медленным операциям, а успешные и быстрые — сэмплировать.
В OTel Collector есть «хвостовое» сэмплирование (tail sampling): решение принять/отбросить трейс принимается после завершения, когда известны итоги (ошибка, задержка). Пример: сохраняем только ошибки и медленные запросы дольше 500 мс.
processors:
tail_sampling:
decision_wait: 5s
num_traces: 50000
policies:
- name: errors
type: status_code
status_code:
status_codes: [ERROR]
- name: slow
type: latency
latency:
threshold_ms: 500
service:
pipelines:
traces:
receivers: [otlp]
processors: [tail_sampling, batch]
exporters: [jaeger]
Так вы концентрируете хранилище на проблемных запросах и существенно снижаете расходы.
Не отправляйте персональные данные пользователя в трейсы и логи. Удаляйте или маскируйте их на коллекторе.
processors:
attributes:
actions:
- key: http.request.header.authorization
action: delete
- key: user.email
action: delete
- key: card.pan
action: delete
service:
pipelines:
traces:
receivers: [otlp]
processors: [attributes, batch]
exporters: [jaeger]
Это снижает риски утечек и упрощает соответствие требованиям (например, локальным политикам безопасности, PCI DSS и пр.).
Минимальный стандарт: всегда прокидывайте trace_id в логи. Улучшение: отправляйте логи в систему, где кликом можно перейти к трейсу (например, через поле trace_id). Ещё лучше: добавьте бизнес‑метрики в спаны (стоимость корзины, тариф, тип клиента), но без персональных данных — это помогает отличать «медленно для всех» от «медленно для VIP‑сегмента».
Пример для Node.js (облегчённо):
const { context, propagation } = require('@opentelemetry/api');
// Отправитель
const carrier = {};
propagation.inject(context.active(), carrier); // carrier кладите в заголовки сообщения
sendToQueue({ body: data, headers: carrier });
// Получатель
function onMessage(msg) {
const ctx = propagation.extract(context.active(), msg.headers || {});
// Запускаем работу внутри восстановленного контекста
context.with(ctx, () => processMessage(msg.body));
}
Мини‑дашборд в UI трейсера: «топ медленных маршрутов», «топ ошибок по зависимостям», «сравнение версий релизов по задержке».
OpenTelemetry поддерживает контекст и для фоновых задач. Ключ — явная инъекция/извлечение контекста в заголовки сообщения (пример выше). Для планировщиков (cron/джобы) задавайте родительский спан вручную и связывайте шаги (скачал → обработал → сохранил → отправил уведомление). Это позволит видеть конец‑в‑конец и фоновые цепочки, которые часто скрыты от глаз.
Бонус для очередей с повторной доставкой: добавьте атрибуты попытки и причину ретрая — так вы быстрее распознаете зацикливание и неправильные пороги таймаутов.
OpenTelemetry даёт сквозную наблюдаемость без привязки к одному вендору и без «чёрных ящиков». С относительно небольшими усилиями вы получаете:
Начните с пилота на одном сервисе, добавьте хвостовое сэмплирование и привяжите логи к trace_id. Через квартал у вас будет система, которая окупается за счёт экономии времени инженеров и более стабильных релизов.