С помощью OpenTelemetry можно собирать трассировки и отправлять их в Tracer. В SDK для этого есть три класса: TracerSpanProcessor добавляет атрибуты Tracer, TracerUploadSpanExporter отправляет спаны, а TracerContextManager помогает сохранить связь ошибок с трассировкой. Интеграция доступна в браузере и Node.js. Зависимости OpenTelemetry нужно установить отдельно.
При переходе с отдельного пакета см. руководство по миграции OpenTelemetry.
Примеры используют OpenTelemetry SDK 2.x. Инициализируйте провайдер один раз при запуске приложения.
npm install @apptracer/sdk @opentelemetry/api@1.9.0 @opentelemetry/sdk-trace-web@2.5.0
import { WebTracerProvider, BatchSpanProcessor, StackContextManager } from '@opentelemetry/sdk-trace-web'; import { TracerSpanProcessor, TracerUploadSpanExporter, TracerContextManager, initTracerErrorAsyncStack, } from '@apptracer/sdk'; const provider = new WebTracerProvider({ spanProcessors: [ new TracerSpanProcessor(), new BatchSpanProcessor(new TracerUploadSpanExporter({ appToken: 'appToken', // appToken из настроек проекта в Tracer })), ], }); initTracerErrorAsyncStack(); provider.register({ contextManager: new TracerContextManager(new StackContextManager()), });
Создайте и завершите спан через настроенный выше provider. Полученный tracer используется в следующих примерах:
const tracer = provider.getTracer('checkout'); const span = tracer.startSpan('submit-order'); span.end();
Чтобы передать контекст спана в HTTP-запросе, используйте getTracerTraceparent(spanContext: SpanContext): string | undefined. Функция доступна во всех сборках SDK и работает без инициализации Tracer.
import { getTracerTraceparent } from '@apptracer/sdk'; const span = tracer.startSpan('load-order'); try { const traceparent = getTracerTraceparent(span.spanContext()); const headers: Record<string, string> = {}; if (traceparent !== undefined) headers.traceparent = traceparent; await fetch('/api/orders/123', { headers }); } finally { span.end(); }
Передайте контекст нужного спана через span.spanContext(). При некорректных идентификаторах или флагах функция возвращает undefined.
Для продолжения трейса принимающая сторона должна извлечь traceparent и ис пользовать полученный контекст как родительский. Если требуется также передавать tracestate или baggage, используйте propagation.inject(...) из OpenTelemetry с настроенными propagators.
startSpan() создаёт спан, но не делает его активным. Чтобы связать выполняемый код с этим спаном, используйте startActiveSpan() или context.with(). Если внутри уже активного контекста открыть новый, после выхода из него восстановится предыдущий.
Для отправки ошибок подключите модуль ошибок:
import { initTracerError, initTracerErrorUploader } from '@apptracer/sdk'; const tracerError = initTracerError(); initTracerErrorUploader({ appToken: 'appToken', // appToken из настроек проекта в Tracer versionName: '1.0.0', // версия вашего приложения });
При вызове tracerError.error(error) SDK добавляет traceId / spanId из сохранённого контекста ошибки или текущего активного спана. Без контекста ошибка отправляется без привязки. UUID сессии Tracer и traceId — отдельные идентификаторы.
Для переноса контекста через обработчики Promise, таймеры и addEventListener подключите TracerContextManager вместе с модулем асинхронных стеков. Их можно подключить в любом порядке: контекст будет сохраняться у обработчиков, зарегистрированных после подключения обоих модулей.
Другой менеджер контекста сам по себе не включает этот механизм SDK. Если вы используете AbortSignal в среде без WeakRef, привяжите обработчик к контексту через context.bind(...).
После нативного await браузер может потерять активный контекст. Чтобы связать ошибку с нужной операцией, сохраните её контекст и передайте его в context.with() при регистрации ошибки:
import { context, trace, SpanStatusCode } from '@opentelemetry/api'; async function loadOrder(orderId: string) { const span = tracer.startSpan('load-order'); const requestContext = trace.setSpan(context.active(), span); try { const response = await fetch('/api/orders/' + encodeURIComponent(orderId)); if (!response.ok) throw new Error('HTTP ' + response.status); return await response.json(); } catch (error) { span.setStatus({ code: SpanStatusCode.ERROR }); context.with(requestContext, function () { tracerError.error(error); }); throw error; } finally { span.end(); } }
Завершите спаны перед await provider.forceFlush(): провайдер отправит накопленную очередь и дождётся текущих запросов. При остановке приложения вызовите await provider.shutdown().
TracerSpanProcessor фиксирует атрибуты при старте спана:
apptracer.sessionUuid — текущая сессия Tracer;user.id — текущий пользователь выбранного экземпляра SDK.import { TracerSpanProcessor } from '@apptracer/sdk'; const processor = new TracerSpanProcessor();
Процессор не хранит спаны и сам их не отправляет. После shutdown() он перестаёт добавлять атрибуты.
new TracerUploadSpanExporter({ appToken, apiHost? }) принимает обязательный токен и необязательный API-хост, отправляет спаны через /api/otel/trace. Параметры versionName, versionCode, environment и Node.js tlsAgent из конфигурации обычных uploader-ов сюда не передаются.
Сбор и пакетирование настраиваются в OpenTelemetry, например через BatchSpanProcessor.
export() вызывает обработчик результата после завершения отправки.forceFlush() дожидается запросов, начатых до его вызова, включая повторные отправки.shutdown() прекращает приём новых спанов на отправку и дожидается текущих запросов.Если ожидаемый запрос завершится окончательной ошибкой, Promise от forceFlush() или shutdown() будет отклонён.
Число повторных отправок не ограничено. Пока сервер недоступен и SDK повторяет запросы, ожидание продолжается. Таймаут OpenTelemetry не отменяет запросы Tracer.
disableTracerModules() не останавливает отдельно подключённый экспортёр. Для его завершения используйте методы OpenTelemetry.
SDK не включает полифилы. Если в вашей среде нет Promise или других возможностей, необходимых выбранной версии OpenTelemetry, добавьте их в приложение.