/** * Shared OpenTelemetry tracing utilities * * This module provides helpers for manual span creation and * initializing tracing for standalone processes (like the ingester). */ import { trace, context, SpanStatusCode, type Span } from '@opentelemetry/api'; /** Get a tracer for creating spans */ export function getTracer(name: string = 'papili') { return trace.getTracer(name); } /** Get the current active span */ export function getCurrentSpan(): Span | undefined { return trace.getSpan(context.active()); } /** * Execute a function within a new span */ export async function withSpan( name: string, fn: (span: Span) => Promise, attributes?: Record ): Promise { const tracer = getTracer(); return tracer.startActiveSpan(name, async (span) => { if (attributes) { span.setAttributes(attributes); } try { const result = await fn(span); span.setStatus({ code: SpanStatusCode.OK }); return result; } catch (error) { span.setStatus({ code: SpanStatusCode.ERROR, message: error instanceof Error ? error.message : 'Unknown error' }); span.recordException(error instanceof Error ? error : new Error(String(error))); throw error; } finally { span.end(); } }); } /** * Execute a sync function within a new span */ export function withSpanSync( name: string, fn: (span: Span) => T, attributes?: Record ): T { const tracer = getTracer(); const span = tracer.startSpan(name); if (attributes) { span.setAttributes(attributes); } try { const result = fn(span); span.setStatus({ code: SpanStatusCode.OK }); return result; } catch (error) { span.setStatus({ code: SpanStatusCode.ERROR, message: error instanceof Error ? error.message : 'Unknown error' }); span.recordException(error instanceof Error ? error : new Error(String(error))); throw error; } finally { span.end(); } }