autotel
Version:
Write Once, Observe Anywhere
1 lines • 24.8 kB
Source Map (JSON)
{"version":3,"sources":["../src/messaging-adapters.ts"],"names":["TraceFlags","traceId","spanId","sampled"],"mappings":"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","file":"messaging-adapters.cjs","sourcesContent":["/**\n * Pre-built adapter configurations for common messaging systems.\n *\n * These adapters provide ready-to-use hook configurations for systems\n * not explicitly supported by the core messaging module. Use them with\n * traceProducer/traceConsumer to get system-specific attributes.\n *\n * @example NATS consumer\n * ```typescript\n * import { traceConsumer } from 'autotel/messaging';\n * import { natsAdapter } from 'autotel/messaging/adapters';\n *\n * const processMessage = traceConsumer({\n * system: 'nats',\n * destination: 'orders',\n * ...natsAdapter.consumer,\n * })(ctx => async (msg) => {\n * // msg.subject, msg.info.stream are now captured as span attributes\n * await handleOrder(msg.data);\n * });\n * ```\n *\n * @example Datadog context propagation\n * ```typescript\n * import { traceConsumer } from 'autotel/messaging';\n * import { datadogContextExtractor } from 'autotel/messaging/adapters';\n *\n * const processMessage = traceConsumer({\n * system: 'kafka',\n * destination: 'events',\n * customContextExtractor: datadogContextExtractor,\n * })(ctx => async (msg) => {\n * // Parent span from Datadog trace headers is linked\n * });\n * ```\n *\n * @module\n */\n\nimport type { AttributeValue, SpanContext } from '@opentelemetry/api';\nimport { TraceFlags } from '@opentelemetry/api';\nimport type { ProducerContext, ConsumerContext } from './messaging';\n\n// ============================================================================\n// Adapter Types\n// ============================================================================\n\n/**\n * Producer adapter configuration\n */\nexport interface ProducerAdapter {\n /**\n * Hook to add system-specific attributes to producer spans\n */\n customAttributes?: (\n ctx: ProducerContext,\n args: unknown[],\n ) => Record<string, AttributeValue>;\n\n /**\n * Hook to inject custom headers beyond W3C traceparent\n */\n customHeaders?: (ctx: ProducerContext) => Record<string, string>;\n}\n\n/**\n * Consumer adapter configuration\n */\nexport interface ConsumerAdapter {\n /**\n * Extract headers from the message for trace context propagation\n */\n headersFrom?: (msg: unknown) => Record<string, string> | undefined;\n\n /**\n * Hook to add system-specific attributes to consumer spans\n */\n customAttributes?: (\n ctx: ConsumerContext,\n msg: unknown,\n ) => Record<string, AttributeValue>;\n\n /**\n * Hook to extract parent span context from non-W3C header formats\n */\n customContextExtractor?: (\n headers: Record<string, string>,\n ) => SpanContext | null;\n}\n\n/**\n * Combined producer and consumer adapter\n */\nexport interface MessagingAdapter {\n producer?: ProducerAdapter;\n consumer?: ConsumerAdapter;\n}\n\n// ============================================================================\n// NATS JetStream Adapter\n// ============================================================================\n\n/**\n * NATS JetStream message type (for reference)\n *\n * @internal Not exported - users bring their own NATS types\n */\ninterface NatsJetStreamMsg {\n subject: string;\n reply?: string;\n data: Uint8Array;\n headers?: {\n /** Convert headers to plain object (some NATS implementations) */\n toJSON?: () => Record<string, string> | unknown;\n /** Get a header value by key (Headers-like interface) */\n get?: (key: string) => string | undefined;\n /** Iterate over header entries */\n entries?: () => Iterable<[string, string]>;\n };\n info?: {\n stream: string;\n consumer: string;\n redeliveryCount?: number;\n pending?: number;\n timestampNanos?: bigint;\n };\n}\n\n/**\n * NATS JetStream adapter\n *\n * Captures NATS-specific attributes following NATS observability conventions.\n *\n * @example Producer\n * ```typescript\n * const publishOrder = traceProducer({\n * system: 'nats',\n * destination: 'orders.created',\n * ...natsAdapter.producer,\n * })(ctx => async (subject, payload, opts) => {\n * const headers = ctx.getTraceHeaders();\n * await nc.publish(subject, payload, { headers });\n * });\n * ```\n *\n * @example Consumer\n * ```typescript\n * const processOrder = traceConsumer({\n * system: 'nats',\n * destination: 'orders.created',\n * consumerGroup: 'order-processor',\n * ...natsAdapter.consumer,\n * })(ctx => async (msg: JsMsg) => {\n * await handleOrder(msg.data);\n * msg.ack();\n * });\n * ```\n */\nexport const natsAdapter: MessagingAdapter = {\n producer: {\n customAttributes: (_ctx, args) => {\n const msg = args[0] as\n | { subject?: string; replyTo?: string; stream?: string }\n | undefined;\n const attrs: Record<string, AttributeValue> = {};\n\n if (msg?.subject) attrs['nats.subject'] = msg.subject;\n if (msg?.replyTo) attrs['nats.reply_to'] = msg.replyTo;\n if (msg?.stream) attrs['nats.stream'] = msg.stream;\n\n return attrs;\n },\n },\n consumer: {\n headersFrom: (msg) => {\n const natsMsg = msg as NatsJetStreamMsg;\n const headers = natsMsg.headers;\n\n if (!headers) return;\n\n // Try toJSON() first (some NATS implementations)\n if (typeof headers.toJSON === 'function') {\n const json = headers.toJSON();\n if (json && typeof json === 'object') {\n return json as Record<string, string>;\n }\n }\n\n // Fallback: use .get() for common trace headers\n // This handles Headers-like objects that only expose .get()\n if (typeof headers.get === 'function') {\n const result: Record<string, string> = {};\n const traceHeaders = [\n 'traceparent',\n 'tracestate',\n 'baggage',\n 'x-b3-traceid',\n 'x-b3-spanid',\n 'x-b3-sampled',\n 'b3',\n ];\n for (const key of traceHeaders) {\n const value = headers.get(key);\n if (value) {\n result[key] = value;\n }\n }\n if (Object.keys(result).length > 0) {\n return result;\n }\n }\n\n // Fallback: try to iterate if it's iterable (e.g., entries())\n if (typeof headers.entries === 'function') {\n const result: Record<string, string> = {};\n for (const [key, value] of headers.entries()) {\n if (typeof key === 'string' && typeof value === 'string') {\n result[key] = value;\n }\n }\n if (Object.keys(result).length > 0) {\n return result;\n }\n }\n\n return;\n },\n customAttributes: (_ctx, msg) => {\n const natsMsg = msg as NatsJetStreamMsg;\n const attrs: Record<string, AttributeValue> = {};\n\n if (natsMsg.subject) attrs['nats.subject'] = natsMsg.subject;\n if (natsMsg.reply) attrs['nats.reply_to'] = natsMsg.reply;\n if (natsMsg.info?.stream) attrs['nats.stream'] = natsMsg.info.stream;\n if (natsMsg.info?.consumer)\n attrs['nats.consumer'] = natsMsg.info.consumer;\n if (natsMsg.info?.redeliveryCount !== undefined) {\n attrs['nats.delivered_count'] = natsMsg.info.redeliveryCount;\n }\n if (natsMsg.info?.pending !== undefined) {\n attrs['nats.pending'] = natsMsg.info.pending;\n }\n\n return attrs;\n },\n },\n};\n\n// ============================================================================\n// Temporal Adapter\n// ============================================================================\n\n/**\n * Temporal activity/workflow info type (for reference)\n *\n * @internal Not exported - users bring their own Temporal types\n */\ninterface TemporalActivityInfo {\n workflowId?: string;\n runId?: string;\n activityId?: string;\n taskQueue?: string;\n attempt?: number;\n workflowType?: string;\n activityType?: string;\n startToCloseTimeout?: string;\n scheduleToCloseTimeout?: string;\n}\n\n/**\n * Temporal adapter\n *\n * Captures Temporal-specific attributes for workflow activities.\n * Use this when instrumenting Temporal activity handlers.\n *\n * @example Activity handler\n * ```typescript\n * const processOrder = traceConsumer({\n * system: 'temporal',\n * destination: 'order-activities',\n * ...temporalAdapter.consumer,\n * })(ctx => async (info: ActivityInfo, input: OrderInput) => {\n * // Temporal attributes are captured automatically\n * return processOrderLogic(input);\n * });\n * ```\n *\n * @example Workflow signal/query\n * ```typescript\n * const sendSignal = traceProducer({\n * system: 'temporal',\n * destination: 'order-signals',\n * ...temporalAdapter.producer,\n * })(ctx => async (workflowId, signalName, payload) => {\n * await client.workflow.signal(workflowId, signalName, payload);\n * });\n * ```\n */\nexport const temporalAdapter: MessagingAdapter = {\n producer: {\n customAttributes: (_ctx, args) => {\n const info = args[0] as TemporalActivityInfo | undefined;\n const attrs: Record<string, AttributeValue> = {};\n\n if (info?.workflowId) attrs['temporal.workflow_id'] = info.workflowId;\n if (info?.runId) attrs['temporal.run_id'] = info.runId;\n if (info?.taskQueue) attrs['temporal.task_queue'] = info.taskQueue;\n if (info?.workflowType)\n attrs['temporal.workflow_type'] = info.workflowType;\n\n return attrs;\n },\n },\n consumer: {\n customAttributes: (_ctx, msg) => {\n const info = msg as TemporalActivityInfo;\n const attrs: Record<string, AttributeValue> = {};\n\n if (info.workflowId) attrs['temporal.workflow_id'] = info.workflowId;\n if (info.runId) attrs['temporal.run_id'] = info.runId;\n if (info.activityId) attrs['temporal.activity_id'] = info.activityId;\n if (info.taskQueue) attrs['temporal.task_queue'] = info.taskQueue;\n if (info.attempt !== undefined) attrs['temporal.attempt'] = info.attempt;\n if (info.activityType)\n attrs['temporal.activity_type'] = info.activityType;\n\n return attrs;\n },\n },\n};\n\n// ============================================================================\n// Cloudflare Queues Adapter\n// ============================================================================\n\n/**\n * Cloudflare Queue message type (for reference)\n *\n * @internal Not exported - users bring their own Cloudflare types\n */\ninterface CloudflareQueueMessage {\n id: string;\n timestamp: Date;\n body: unknown;\n attempts: number;\n}\n\n/**\n * Cloudflare Queues adapter\n *\n * Captures Cloudflare Queue-specific attributes.\n *\n * @example Queue consumer\n * ```typescript\n * export default {\n * async queue(batch: MessageBatch, env: Env) {\n * for (const msg of batch.messages) {\n * await processMessage(msg);\n * }\n * },\n * };\n *\n * const processMessage = traceConsumer({\n * system: 'cloudflare_queues',\n * destination: 'my-queue',\n * ...cloudflareQueuesAdapter.consumer,\n * })(ctx => async (msg: Message) => {\n * await handleMessage(msg.body);\n * msg.ack();\n * });\n * ```\n */\nexport const cloudflareQueuesAdapter: MessagingAdapter = {\n consumer: {\n customAttributes: (_ctx, msg) => {\n const cfMsg = msg as CloudflareQueueMessage;\n const attrs: Record<string, AttributeValue> = {};\n\n if (cfMsg.id) attrs['cloudflare.queue.message_id'] = cfMsg.id;\n if (cfMsg.timestamp) {\n attrs['cloudflare.queue.timestamp_ms'] = cfMsg.timestamp.getTime();\n }\n if (cfMsg.attempts !== undefined) {\n attrs['cloudflare.queue.attempts'] = cfMsg.attempts;\n }\n\n return attrs;\n },\n },\n};\n\n// ============================================================================\n// Context Extractors for Non-W3C Formats\n// ============================================================================\n\n/**\n * Datadog trace context extractor\n *\n * Extracts parent span context from Datadog-format trace headers.\n * Converts Datadog's decimal IDs to OpenTelemetry's hex format.\n *\n * Note: Datadog sends trace/span IDs as decimal strings, not hex.\n * This extractor converts decimal -> hex before formatting for OTel.\n *\n * @example\n * ```typescript\n * const processMessage = traceConsumer({\n * system: 'kafka',\n * destination: 'events',\n * customContextExtractor: datadogContextExtractor,\n * })(ctx => async (msg) => {\n * // Links to parent Datadog span automatically\n * });\n * ```\n */\nexport function datadogContextExtractor(\n headers: Record<string, string>,\n): SpanContext | null {\n const traceIdDecimal = headers['x-datadog-trace-id'];\n const spanIdDecimal = headers['x-datadog-parent-id'];\n const samplingPriority = headers['x-datadog-sampling-priority'];\n\n if (!traceIdDecimal || !spanIdDecimal) return null;\n\n // Datadog sends IDs as decimal strings - convert to hex\n // Use BigInt for 64-bit values that exceed Number.MAX_SAFE_INTEGER\n let otelTraceId: string;\n let otelSpanId: string;\n\n try {\n // Convert decimal to hex and pad to OTel format\n // OTel trace IDs are 32 hex chars (128-bit), Datadog uses 64-bit\n otelTraceId = BigInt(traceIdDecimal).toString(16).padStart(32, '0');\n // OTel span IDs are 16 hex chars (64-bit)\n otelSpanId = BigInt(spanIdDecimal).toString(16).padStart(16, '0');\n } catch {\n // Invalid decimal string\n return null;\n }\n\n // Sampling priority > 0 means sampled\n const sampled = samplingPriority\n ? Number.parseInt(samplingPriority, 10) > 0\n : true;\n\n return {\n traceId: otelTraceId,\n spanId: otelSpanId,\n traceFlags: sampled ? TraceFlags.SAMPLED : TraceFlags.NONE,\n isRemote: true,\n };\n}\n\n/**\n * B3 (Zipkin) trace context extractor\n *\n * Extracts parent span context from B3 format headers.\n * Supports both single-header (b3) and multi-header formats.\n *\n * @see https://github.com/openzipkin/b3-propagation\n *\n * @example Single-header format\n * ```typescript\n * // Header: b3: 80f198ee56343ba864fe8b2a57d3eff7-e457b5a2e4d86bd1-1\n * const processMessage = traceConsumer({\n * system: 'rabbitmq',\n * destination: 'events',\n * customContextExtractor: b3ContextExtractor,\n * })(ctx => async (msg) => {\n * // Links to parent Zipkin span\n * });\n * ```\n *\n * @example Multi-header format\n * ```typescript\n * // Headers: X-B3-TraceId, X-B3-SpanId, X-B3-Sampled\n * ```\n */\nexport function b3ContextExtractor(\n headers: Record<string, string>,\n): SpanContext | null {\n // Try single-header format first: {TraceId}-{SpanId}-{SamplingState}-{ParentSpanId}\n const b3Single = headers['b3'] || headers['B3'];\n if (b3Single) {\n // Handle \"0\" (not sampled, no trace) case\n if (b3Single === '0') return null;\n\n const parts = b3Single.split('-');\n const traceId = parts[0];\n const spanId = parts[1];\n const sampledFlag = parts[2];\n\n if (traceId && spanId) {\n const sampled = sampledFlag !== '0' && sampledFlag !== 'd';\n\n return {\n traceId: traceId.padStart(32, '0'),\n spanId: spanId.padStart(16, '0'),\n traceFlags: sampled ? TraceFlags.SAMPLED : TraceFlags.NONE,\n isRemote: true,\n };\n }\n }\n\n // Fall back to multi-header format\n const traceId =\n headers['x-b3-traceid'] ||\n headers['X-B3-TraceId'] ||\n headers['X-B3-Traceid'];\n const spanId =\n headers['x-b3-spanid'] || headers['X-B3-SpanId'] || headers['X-B3-Spanid'];\n const sampledHeader =\n headers['x-b3-sampled'] ||\n headers['X-B3-Sampled'] ||\n headers['x-b3-flags'] ||\n headers['X-B3-Flags'];\n\n if (!traceId || !spanId) return null;\n\n // x-b3-sampled: \"1\" or \"true\" = sampled, \"0\" or \"false\" = not sampled\n // x-b3-flags: \"1\" = debug (implies sampled)\n const sampled =\n sampledHeader === '1' ||\n sampledHeader === 'true' ||\n sampledHeader === undefined; // Default to sampled if not specified\n\n return {\n traceId: traceId.padStart(32, '0'),\n spanId: spanId.padStart(16, '0'),\n traceFlags: sampled ? TraceFlags.SAMPLED : TraceFlags.NONE,\n isRemote: true,\n };\n}\n\n/**\n * AWS X-Ray trace context extractor\n *\n * Extracts parent span context from AWS X-Ray trace header.\n * Format: Root=1-{timestamp}-{random};Parent={parent-id};Sampled={0|1}\n *\n * @example\n * ```typescript\n * const processMessage = traceConsumer({\n * system: 'sqs',\n * destination: 'my-queue',\n * customContextExtractor: xrayContextExtractor,\n * })(ctx => async (msg) => {\n * // Links to parent X-Ray trace\n * });\n * ```\n */\nexport function xrayContextExtractor(\n headers: Record<string, string>,\n): SpanContext | null {\n const xrayHeader = headers['x-amzn-trace-id'] || headers['X-Amzn-Trace-Id'];\n\n if (!xrayHeader) return null;\n\n // Parse: Root=1-{8-char-timestamp}-{24-char-random};Parent={16-char-parent};Sampled=1\n const rootMatch = xrayHeader.match(/Root=1-([a-f0-9]{8})-([a-f0-9]{24})/i);\n const parentMatch = xrayHeader.match(/Parent=([a-f0-9]{16})/i);\n const sampledMatch = xrayHeader.match(/Sampled=([01])/);\n\n if (!rootMatch || !parentMatch) return null;\n\n // X-Ray trace ID format: 1-{timestamp}-{random} -> OTel: {timestamp}{random}\n const timestamp = rootMatch[1];\n const random = rootMatch[2];\n const parentId = parentMatch[1];\n\n if (!timestamp || !random || !parentId) return null;\n\n const traceId = `${timestamp}${random}`;\n const spanId = parentId;\n const sampled = sampledMatch ? sampledMatch[1] === '1' : true;\n\n return {\n traceId,\n spanId,\n traceFlags: sampled ? TraceFlags.SAMPLED : TraceFlags.NONE,\n isRemote: true,\n };\n}\n"]}