UNPKG

@temporalio/interceptors-opentelemetry

Version:
83 lines (75 loc) 2.56 kB
/** * opentelemetry instrumentation helper functions * @module */ import * as otel from '@opentelemetry/api'; import { Headers, defaultPayloadConverter } from '@temporalio/common'; /** Default trace header for opentelemetry interceptors */ export const TRACE_HEADER = '_tracer-data'; /** As in workflow run id */ export const RUN_ID_ATTR_KEY = 'run_id'; const payloadConverter = defaultPayloadConverter; /** * If found, return an otel Context deserialized from the provided headers */ export async function extractContextFromHeaders(headers: Headers): Promise<otel.Context | undefined> { const encodedSpanContext = headers[TRACE_HEADER]; if (encodedSpanContext === undefined) { return undefined; } const textMap: Record<string, string> = payloadConverter.fromPayload(encodedSpanContext); return otel.propagation.extract(otel.context.active(), textMap, otel.defaultTextMapGetter); } /** * Given headers, return new headers with the current otel context inserted */ export async function headersWithContext(headers: Headers): Promise<Headers> { const carrier = {}; otel.propagation.inject(otel.context.active(), carrier, otel.defaultTextMapSetter); return { ...headers, [TRACE_HEADER]: payloadConverter.toPayload(carrier) }; } async function wrapWithSpan<T>( span: otel.Span, fn: (span: otel.Span) => Promise<T>, acceptableErrors?: (err: unknown) => boolean ): Promise<T> { try { const ret = await fn(span); span.setStatus({ code: otel.SpanStatusCode.OK }); return ret; } catch (err: any) { if (acceptableErrors === undefined || !acceptableErrors(err)) { span.setStatus({ code: otel.SpanStatusCode.ERROR }); span.recordException(err); } else { span.setStatus({ code: otel.SpanStatusCode.OK }); } throw err; } finally { span.end(); } } export interface InstrumentOptions<T> { tracer: otel.Tracer; spanName: string; fn: (span: otel.Span) => Promise<T>; context?: otel.Context; acceptableErrors?: (err: unknown) => boolean; } /** * Wraps `fn` in a span which ends when function returns or throws */ export async function instrument<T>({ tracer, spanName, fn, context, acceptableErrors, }: InstrumentOptions<T>): Promise<T> { if (context) { return await otel.context.with(context, async () => { return await tracer.startActiveSpan(spanName, async (span) => await wrapWithSpan(span, fn, acceptableErrors)); }); } return await tracer.startActiveSpan(spanName, async (span) => await wrapWithSpan(span, fn, acceptableErrors)); }