autotel 4.1.0 → 4.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. package/dist/auto.cjs +5 -3
  2. package/dist/auto.cjs.map +1 -1
  3. package/dist/auto.js +3 -3
  4. package/dist/auto.js.map +1 -1
  5. package/dist/chunk-C_NdSu1c.cjs +34 -0
  6. package/dist/correlation-id.cjs +1 -1
  7. package/dist/correlation-id.d.cts.map +1 -1
  8. package/dist/correlation-id.d.ts.map +1 -1
  9. package/dist/correlation-id.js +1 -1
  10. package/dist/decorators.cjs +1 -1
  11. package/dist/decorators.js +1 -1
  12. package/dist/{event-ByBTV9M2.js → event-531asIM6.js} +4 -4
  13. package/dist/{event-ByBTV9M2.js.map → event-531asIM6.js.map} +1 -1
  14. package/dist/{event-BhHREDJk.cjs → event-CcZYwp50.cjs} +4 -4
  15. package/dist/{event-BhHREDJk.cjs.map → event-CcZYwp50.cjs.map} +1 -1
  16. package/dist/event.cjs +1 -1
  17. package/dist/event.js +1 -1
  18. package/dist/{functional-zpzNLhky.cjs → functional-C8B0Qa7o.cjs} +10 -7
  19. package/dist/functional-C8B0Qa7o.cjs.map +1 -0
  20. package/dist/{functional-DtI0u4vx.js → functional-r-AUIRy_.js} +9 -9
  21. package/dist/functional-r-AUIRy_.js.map +1 -0
  22. package/dist/functional.cjs +1 -1
  23. package/dist/functional.js +1 -1
  24. package/dist/http.cjs +1 -1
  25. package/dist/http.js +1 -1
  26. package/dist/index.cjs +15 -13
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.cts.map +1 -1
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +14 -14
  31. package/dist/index.js.map +1 -1
  32. package/dist/{init-D-jnNMix.js → init-BS2JVkrL.js} +2 -2
  33. package/dist/{init-D-jnNMix.js.map → init-BS2JVkrL.js.map} +1 -1
  34. package/dist/{init-BX7AmFRl.cjs → init-BXiuPK6j.cjs} +3 -3
  35. package/dist/{init-BX7AmFRl.cjs.map → init-BXiuPK6j.cjs.map} +1 -1
  36. package/dist/instrumentation.cjs +2 -2
  37. package/dist/instrumentation.js +2 -2
  38. package/dist/logger.cjs +236 -8
  39. package/dist/logger.cjs.map +1 -0
  40. package/dist/messaging.cjs +1 -1
  41. package/dist/messaging.js +1 -1
  42. package/dist/{node-require-DF5QBX6z.cjs → node-require-CZ_PU448.cjs} +6 -4
  43. package/dist/node-require-CZ_PU448.cjs.map +1 -0
  44. package/dist/{node-require-Db1oDpLj.js → node-require-vROmTeJ8.js} +5 -5
  45. package/dist/node-require-vROmTeJ8.js.map +1 -0
  46. package/dist/{operation-context-C-2hmmtP.js → operation-context-CKBoA4Qy.js} +3 -3
  47. package/dist/operation-context-CKBoA4Qy.js.map +1 -0
  48. package/dist/{operation-context-n4_obUwq.cjs → operation-context-D6LDf4W_.cjs} +3 -1
  49. package/dist/operation-context-D6LDf4W_.cjs.map +1 -0
  50. package/dist/register.cjs +3 -1
  51. package/dist/register.cjs.map +1 -1
  52. package/dist/register.js +2 -2
  53. package/dist/register.js.map +1 -1
  54. package/dist/semantic-helpers.cjs +1 -1
  55. package/dist/semantic-helpers.js +1 -1
  56. package/dist/{stable-hash-Cg5cT34Q.js → stable-hash-ChFBIhNt.js} +3 -3
  57. package/dist/stable-hash-ChFBIhNt.js.map +1 -0
  58. package/dist/{stable-hash-BNTMrmdB.cjs → stable-hash-brKISGf1.cjs} +4 -2
  59. package/dist/stable-hash-brKISGf1.cjs.map +1 -0
  60. package/dist/trace-context-Cijqoi6e.d.cts.map +1 -1
  61. package/dist/trace-context-Cijqoi6e.d.ts.map +1 -1
  62. package/dist/trace-helpers.cjs +1 -1
  63. package/dist/trace-helpers.js +1 -1
  64. package/dist/{track-wc0HafS_.js → track-COUuU48p.js} +5 -5
  65. package/dist/track-COUuU48p.js.map +1 -0
  66. package/dist/{track-D59FfpL0.cjs → track-Cb3Q4QmS.cjs} +4 -2
  67. package/dist/track-Cb3Q4QmS.cjs.map +1 -0
  68. package/dist/validate.cjs +1 -1
  69. package/dist/validate.js +1 -1
  70. package/dist/webhook.cjs +1 -1
  71. package/dist/webhook.js +1 -1
  72. package/dist/workflow-distributed.cjs +1 -1
  73. package/dist/workflow-distributed.js +1 -1
  74. package/dist/workflow.cjs +3 -1
  75. package/dist/workflow.cjs.map +1 -1
  76. package/dist/workflow.d.cts.map +1 -1
  77. package/dist/workflow.d.ts.map +1 -1
  78. package/dist/workflow.js +3 -3
  79. package/dist/workflow.js.map +1 -1
  80. package/dist/yaml-config.cjs +233 -4
  81. package/dist/yaml-config.cjs.map +1 -0
  82. package/dist/yaml-config.d.cts.map +1 -1
  83. package/dist/yaml-config.d.ts.map +1 -1
  84. package/dist/yaml-config.js +8 -7
  85. package/dist/yaml-config.js.map +1 -1
  86. package/package.json +1 -2
  87. package/dist/functional-DtI0u4vx.js.map +0 -1
  88. package/dist/functional-zpzNLhky.cjs.map +0 -1
  89. package/dist/logger-thMPLpOG.cjs +0 -487
  90. package/dist/logger-thMPLpOG.cjs.map +0 -1
  91. package/dist/node-require-DF5QBX6z.cjs.map +0 -1
  92. package/dist/node-require-Db1oDpLj.js.map +0 -1
  93. package/dist/operation-context-C-2hmmtP.js.map +0 -1
  94. package/dist/operation-context-n4_obUwq.cjs.map +0 -1
  95. package/dist/stable-hash-BNTMrmdB.cjs.map +0 -1
  96. package/dist/stable-hash-Cg5cT34Q.js.map +0 -1
  97. package/dist/track-D59FfpL0.cjs.map +0 -1
  98. package/dist/track-wc0HafS_.js.map +0 -1
  99. package/dist/yaml-config-Ck2uB0Dp.cjs +0 -273
  100. package/dist/yaml-config-Ck2uB0Dp.cjs.map +0 -1
  101. package/src/attribute-redacting-processor.test.ts +0 -763
  102. package/src/attribute-redacting-processor.ts +0 -621
  103. package/src/attributes/attachers.ts +0 -161
  104. package/src/attributes/builders.ts +0 -529
  105. package/src/attributes/domains.ts +0 -42
  106. package/src/attributes/index.ts +0 -81
  107. package/src/attributes/registry.ts +0 -323
  108. package/src/attributes/types.ts +0 -211
  109. package/src/attributes/utils.ts +0 -64
  110. package/src/attributes/validators.ts +0 -266
  111. package/src/attributes.test.ts +0 -292
  112. package/src/auto.ts +0 -67
  113. package/src/autotel-logger.test.ts +0 -548
  114. package/src/autotel-logger.ts +0 -364
  115. package/src/baggage-span-processor.test.ts +0 -202
  116. package/src/baggage-span-processor.ts +0 -100
  117. package/src/business-baggage.test.ts +0 -500
  118. package/src/business-baggage.ts +0 -669
  119. package/src/circuit-breaker.test.ts +0 -341
  120. package/src/circuit-breaker.ts +0 -184
  121. package/src/config.test.ts +0 -94
  122. package/src/config.ts +0 -172
  123. package/src/correlated-events.test.ts +0 -151
  124. package/src/correlated-events.ts +0 -47
  125. package/src/correlation-id.test.ts +0 -163
  126. package/src/correlation-id.ts +0 -206
  127. package/src/db.test.ts +0 -252
  128. package/src/db.ts +0 -447
  129. package/src/decorators.test.ts +0 -153
  130. package/src/decorators.ts +0 -188
  131. package/src/define-event.test.ts +0 -41
  132. package/src/define-event.ts +0 -58
  133. package/src/devtools.ts +0 -60
  134. package/src/drain-pipeline.test.ts +0 -68
  135. package/src/drain-pipeline.ts +0 -199
  136. package/src/drain-toolkit.test.ts +0 -113
  137. package/src/drain-toolkit.ts +0 -129
  138. package/src/enricher-toolkit.test.ts +0 -67
  139. package/src/enricher-toolkit.ts +0 -79
  140. package/src/enrichers.test.ts +0 -150
  141. package/src/enrichers.ts +0 -145
  142. package/src/env-config.test.ts +0 -323
  143. package/src/env-config.ts +0 -309
  144. package/src/error-catalog.test.ts +0 -133
  145. package/src/error-catalog.ts +0 -262
  146. package/src/event-queue.test.ts +0 -864
  147. package/src/event-queue.ts +0 -699
  148. package/src/event-subscriber.ts +0 -262
  149. package/src/event-testing.ts +0 -197
  150. package/src/event.test.ts +0 -1104
  151. package/src/event.ts +0 -988
  152. package/src/events-config.ts +0 -235
  153. package/src/exporters.ts +0 -165
  154. package/src/filtering-span-processor.test.ts +0 -281
  155. package/src/filtering-span-processor.ts +0 -111
  156. package/src/flatten-attributes.test.ts +0 -76
  157. package/src/flatten-attributes.ts +0 -80
  158. package/src/functional.strict-types.typecheck.ts +0 -53
  159. package/src/functional.test.ts +0 -1464
  160. package/src/functional.ts +0 -2539
  161. package/src/functional.types.test.ts +0 -135
  162. package/src/hook.mjs +0 -15
  163. package/src/http.test.ts +0 -485
  164. package/src/http.ts +0 -424
  165. package/src/index.ts +0 -433
  166. package/src/init-auto-redactor.test.ts +0 -53
  167. package/src/init-redactor.test.ts +0 -8
  168. package/src/init.customization.test.ts +0 -665
  169. package/src/init.integrations.test.ts +0 -399
  170. package/src/init.openllmetry.test.ts +0 -194
  171. package/src/init.protocol.test.ts +0 -215
  172. package/src/init.ts +0 -2439
  173. package/src/instrumentation.test.ts +0 -108
  174. package/src/instrumentation.ts +0 -319
  175. package/src/logger.test.ts +0 -125
  176. package/src/logger.ts +0 -341
  177. package/src/messaging-adapters.test.ts +0 -595
  178. package/src/messaging-adapters.ts +0 -583
  179. package/src/messaging-testing.test.ts +0 -573
  180. package/src/messaging-testing.ts +0 -935
  181. package/src/messaging.test.ts +0 -1646
  182. package/src/messaging.ts +0 -2245
  183. package/src/metric-helpers.ts +0 -47
  184. package/src/metric-testing.ts +0 -197
  185. package/src/metric.ts +0 -446
  186. package/src/metrics.test.ts +0 -241
  187. package/src/node-require.ts +0 -123
  188. package/src/operation-context.ts +0 -93
  189. package/src/parse-error.test.ts +0 -73
  190. package/src/parse-error.ts +0 -112
  191. package/src/posthog-logs.test.ts +0 -115
  192. package/src/posthog-logs.ts +0 -77
  193. package/src/pretty-console-exporter.test.ts +0 -545
  194. package/src/pretty-console-exporter.ts +0 -413
  195. package/src/pretty-log-formatter.test.ts +0 -123
  196. package/src/pretty-log-formatter.ts +0 -210
  197. package/src/processors/canonical-log-line-processor.test.ts +0 -523
  198. package/src/processors/canonical-log-line-processor.ts +0 -396
  199. package/src/processors.ts +0 -152
  200. package/src/rate-limiter.test.ts +0 -199
  201. package/src/rate-limiter.ts +0 -98
  202. package/src/redact-values.test.ts +0 -90
  203. package/src/redact-values.ts +0 -34
  204. package/src/register.ts +0 -37
  205. package/src/request-logger.test.ts +0 -545
  206. package/src/request-logger.ts +0 -342
  207. package/src/sampling.test.ts +0 -1060
  208. package/src/sampling.ts +0 -737
  209. package/src/security-schema.test.ts +0 -45
  210. package/src/security-schema.ts +0 -107
  211. package/src/semantic-conventions.ts +0 -15
  212. package/src/semantic-helpers.test.ts +0 -226
  213. package/src/semantic-helpers.ts +0 -438
  214. package/src/shutdown.test.ts +0 -364
  215. package/src/shutdown.ts +0 -246
  216. package/src/span-name-normalizer.test.ts +0 -377
  217. package/src/span-name-normalizer.ts +0 -213
  218. package/src/stable-hash.ts +0 -27
  219. package/src/structured-error.test.ts +0 -191
  220. package/src/structured-error.ts +0 -157
  221. package/src/stub.integration.test.ts +0 -361
  222. package/src/tail-sampling-processor.test.ts +0 -230
  223. package/src/tail-sampling-processor.ts +0 -55
  224. package/src/test-span-collector.test.ts +0 -234
  225. package/src/test-span-collector.ts +0 -150
  226. package/src/testing.ts +0 -705
  227. package/src/trace-context.test.ts +0 -73
  228. package/src/trace-context.ts +0 -567
  229. package/src/trace-helpers.new.test.ts +0 -278
  230. package/src/trace-helpers.test.ts +0 -290
  231. package/src/trace-helpers.ts +0 -710
  232. package/src/trace-hybrid.test.ts +0 -42
  233. package/src/trace-hybrid.ts +0 -37
  234. package/src/tracer-provider.test.ts +0 -183
  235. package/src/tracer-provider.ts +0 -266
  236. package/src/track.test.ts +0 -154
  237. package/src/track.ts +0 -216
  238. package/src/validate.test.ts +0 -287
  239. package/src/validate.ts +0 -307
  240. package/src/validation-attributes.ts +0 -43
  241. package/src/validation.test.ts +0 -330
  242. package/src/validation.ts +0 -246
  243. package/src/variable-name-inference.test.ts +0 -178
  244. package/src/variable-name-inference.ts +0 -242
  245. package/src/webhook.test.ts +0 -649
  246. package/src/webhook.ts +0 -637
  247. package/src/workflow-distributed.test.ts +0 -786
  248. package/src/workflow-distributed.ts +0 -916
  249. package/src/workflow.async-safety.integration.test.ts +0 -345
  250. package/src/workflow.test.ts +0 -647
  251. package/src/workflow.ts +0 -810
  252. package/src/yaml-config.test.ts +0 -373
  253. package/src/yaml-config.ts +0 -351
@@ -1,710 +0,0 @@
1
- /**
2
- * Trace context helpers - Core primitives for trace correlation
3
- *
4
- * These are the building blocks that allow users to bring their own logger
5
- * (bunyan, log4js, custom, etc.) and add trace correlation.
6
- *
7
- * @example Using with bunyan
8
- * ```typescript
9
- * import bunyan from 'bunyan';
10
- * import { enrichWithTraceContext } from 'autotel/trace-helpers';
11
- *
12
- * const bunyanLogger = bunyan.createLogger({ name: 'myapp' });
13
- *
14
- * const logger = {
15
- * info: (msg: string, extra?: object) => {
16
- * bunyanLogger.info(enrichWithTraceContext(extra || {}), msg);
17
- * }
18
- * };
19
- * ```
20
- *
21
- * @example Using with log4js
22
- * ```typescript
23
- * import log4js from 'log4js';
24
- * import { getTraceContext } from 'autotel/trace-helpers';
25
- *
26
- * const log4jsLogger = log4js.getLogger();
27
- *
28
- * function logWithTrace(level: string, msg: string, extra?: object) {
29
- * const context = getTraceContext();
30
- * log4jsLogger[level](msg, { ...extra, ...context });
31
- * }
32
- * ```
33
- */
34
-
35
- import { trace, context, SpanStatusCode } from '@opentelemetry/api';
36
- import type { Span, Tracer, Context } from '@opentelemetry/api';
37
- import { requireModule } from './node-require';
38
-
39
- /**
40
- * WeakMap to store span names for active spans
41
- * This allows us to retrieve the span name even though OpenTelemetry
42
- * doesn't expose it through the public API
43
- */
44
- const spanNameMap = new WeakMap<Span, string>();
45
-
46
- /**
47
- * Store span name for a given span
48
- * Called internally when spans are created
49
- */
50
- export function setSpanName(span: Span, name: string): void {
51
- spanNameMap.set(span, name);
52
- }
53
-
54
- /**
55
- * Trace context extracted from active span
56
- */
57
- export interface TraceContext {
58
- /** Full 32-character hex trace ID */
59
- traceId: string;
60
- /** 16-character hex span ID */
61
- spanId: string;
62
- /** First 16 characters of trace ID (for log grouping/correlation) */
63
- correlationId: string;
64
- /** Function/operation name (OpenTelemetry semantic convention: code.function) */
65
- 'code.function'?: string;
66
- /** Datadog trace ID in decimal format (lower 64 bits) for log-trace correlation */
67
- 'dd.trace_id'?: string;
68
- /** Datadog span ID in decimal format for log-trace correlation */
69
- 'dd.span_id'?: string;
70
- }
71
-
72
- /**
73
- * Convert hex string to decimal string representation
74
- * Handles 64-bit unsigned integers for Datadog correlation
75
- *
76
- * @param hex - Hex string (up to 16 characters for 64-bit)
77
- * @returns Decimal string representation
78
- */
79
- function hexToDecimal(hex: string): string {
80
- // For 64-bit values, use BigInt to avoid precision loss
81
- return BigInt('0x' + hex).toString(10);
82
- }
83
-
84
- /**
85
- * Get current trace context from active span
86
- *
87
- * Returns null if no span is active (e.g., outside of trace operation)
88
- *
89
- * Includes both OpenTelemetry standard fields (hex) and Datadog-specific
90
- * fields (decimal) for maximum compatibility.
91
- *
92
- * @returns Trace context with traceId, spanId, correlationId, and Datadog decimal IDs, or null
93
- *
94
- * @example
95
- * ```typescript
96
- * import { getTraceContext } from 'autotel/trace-helpers';
97
- *
98
- * const context = getTraceContext();
99
- * if (context) {
100
- * console.log('Current trace:', context.traceId);
101
- * // Current trace: 4bf92f3577b34da6a3ce929d0e0e4736
102
- * console.log('Datadog trace ID:', context['dd.trace_id']);
103
- * // Datadog trace ID: 12007117331170166582 (decimal for log correlation)
104
- * }
105
- * ```
106
- */
107
- export function getTraceContext(): TraceContext | null {
108
- const span = trace.getActiveSpan();
109
- if (!span) return null;
110
-
111
- const spanContext = span.spanContext();
112
- const traceId = spanContext.traceId;
113
- const spanId = spanContext.spanId;
114
-
115
- // Get span name from WeakMap (set when span is created)
116
- // Map to OpenTelemetry semantic convention: code.function
117
- const spanName = spanNameMap.get(span);
118
-
119
- // Datadog uses the lower 64 bits of the 128-bit OpenTelemetry trace ID
120
- // Convert from hex to decimal for Datadog's log-trace correlation
121
- const traceIdLower64 = traceId.slice(-16); // Last 16 hex chars = lower 64 bits
122
- const ddTraceId = hexToDecimal(traceIdLower64);
123
- const ddSpanId = hexToDecimal(spanId);
124
-
125
- return {
126
- traceId,
127
- spanId,
128
- correlationId: traceId.slice(0, 16),
129
- ...(spanName && { 'code.function': spanName }),
130
- // Datadog-specific fields for log-trace correlation
131
- 'dd.trace_id': ddTraceId,
132
- 'dd.span_id': ddSpanId,
133
- };
134
- }
135
-
136
- /**
137
- * Enrich object with trace context (traceId, spanId, correlationId, and Datadog fields)
138
- *
139
- * If no span is active, returns the object unchanged.
140
- * This prevents "undefined" or "null" values in logs.
141
- *
142
- * Automatically adds both OpenTelemetry standard fields (hex) and Datadog-specific
143
- * fields (decimal) for maximum compatibility with observability backends.
144
- *
145
- * @param obj - Object to enrich (e.g., log metadata)
146
- * @returns Object with trace context merged in, or unchanged if no active span
147
- *
148
- * @example
149
- * ```typescript
150
- * import { enrichWithTraceContext } from 'autotel/trace-helpers';
151
- *
152
- * // Inside a trace operation:
153
- * const enriched = enrichWithTraceContext({ userId: '123' });
154
- * // {
155
- * // userId: '123',
156
- * // traceId: '4bf92f3577b34da6a3ce929d0e0e4736',
157
- * // spanId: '00f067aa0ba902b7',
158
- * // correlationId: '4bf92f3577b34da6',
159
- * // 'dd.trace_id': '12007117331170166582', // Datadog decimal format
160
- * // 'dd.span_id': '67667974448284583' // Datadog decimal format
161
- * // }
162
- *
163
- * // Outside trace operation:
164
- * const unchanged = enrichWithTraceContext({ userId: '123' });
165
- * // { userId: '123' } - no trace fields added
166
- * ```
167
- */
168
- export function enrichWithTraceContext<T extends Record<string, unknown>>(
169
- obj: T,
170
- ): T {
171
- const context = getTraceContext();
172
- return context ? ({ ...obj, ...context } as T) : obj;
173
- }
174
-
175
- /**
176
- * Check if currently in a trace context
177
- *
178
- * Useful for conditional logic based on trace presence
179
- *
180
- * @returns true if active span exists, false otherwise
181
- *
182
- * @example
183
- * ```typescript
184
- * import { isTracing } from 'autotel/trace-helpers';
185
- *
186
- * if (isTracing()) {
187
- * // Add expensive debug metadata only when tracing
188
- * logger.debug('Detailed context', expensiveDebugData());
189
- * }
190
- * ```
191
- */
192
- export function isTracing(): boolean {
193
- return trace.getActiveSpan() !== undefined;
194
- }
195
-
196
- /**
197
- * Get a tracer instance for creating custom spans
198
- *
199
- * Use this when you need low-level control over span lifecycle.
200
- * For most use cases, prefer trace(), span(), or instrument() instead.
201
- *
202
- * @param name - Tracer name (usually your service or module name)
203
- * @param version - Optional version string
204
- * @returns OpenTelemetry Tracer instance
205
- *
206
- * @example Basic usage
207
- * ```typescript
208
- * import { getTracer } from 'autotel';
209
- *
210
- * const tracer = getTracer('my-service');
211
- * const span = tracer.startSpan('custom.operation');
212
- * try {
213
- * // Your logic
214
- * span.setAttribute('key', 'value');
215
- * } finally {
216
- * span.end();
217
- * }
218
- * ```
219
- *
220
- * @example With AI SDK
221
- * ```typescript
222
- * import { getTracer } from 'autotel';
223
- * import { generateText } from 'ai';
224
- *
225
- * const tracer = getTracer('ai-agent');
226
- * const result = await generateText({
227
- * model: myModel,
228
- * prompt: 'Hello',
229
- * experimental_telemetry: {
230
- * isEnabled: true,
231
- * tracer,
232
- * },
233
- * });
234
- * ```
235
- */
236
- export function getTracer(name: string, version?: string): Tracer {
237
- return trace.getTracer(name, version);
238
- }
239
-
240
- /**
241
- * Get the currently active span
242
- *
243
- * Returns undefined if no span is currently active.
244
- * Useful for adding attributes to the current span.
245
- *
246
- * @returns Active span or undefined
247
- *
248
- * @example Adding attributes to active span
249
- * ```typescript
250
- * import { getActiveSpan } from 'autotel';
251
- *
252
- * const span = getActiveSpan();
253
- * if (span) {
254
- * span.setAttribute('user.id', userId);
255
- * span.setAttribute('user.action', 'click');
256
- * }
257
- * ```
258
- *
259
- * @example Checking span status
260
- * ```typescript
261
- * import { getActiveSpan, SpanStatusCode } from 'autotel';
262
- *
263
- * const span = getActiveSpan();
264
- * if (span?.isRecording()) {
265
- * span.setStatus({ code: SpanStatusCode.OK });
266
- * }
267
- * ```
268
- */
269
- export function getActiveSpan(): Span | undefined {
270
- return trace.getActiveSpan();
271
- }
272
-
273
- /**
274
- * Get the currently active OpenTelemetry context
275
- *
276
- * The context contains the active span and any baggage.
277
- * Useful for context propagation and custom instrumentation.
278
- *
279
- * @returns Current active context
280
- *
281
- * @example Propagating context
282
- * ```typescript
283
- * import { getActiveContext } from 'autotel';
284
- *
285
- * const currentContext = getActiveContext();
286
- * // Pass context to another function or service
287
- * ```
288
- *
289
- * @example With context injection
290
- * ```typescript
291
- * import { getActiveContext, injectTraceContext } from 'autotel';
292
- *
293
- * const headers = {};
294
- * injectTraceContext(headers);
295
- * // Headers now contain trace propagation data
296
- * ```
297
- */
298
- export function getActiveContext(): Context {
299
- // Check stored context first (from baggage setters), then fall back to active context
300
- // This ensures ctx.setBaggage() changes are visible to OpenTelemetry operations
301
- try {
302
- const { getActiveContextWithBaggage } = requireModule<{
303
- getActiveContextWithBaggage: () => Context;
304
- }>('./trace-context');
305
- return getActiveContextWithBaggage();
306
- } catch {
307
- // Fallback if trace-context isn't available
308
- return context.active();
309
- }
310
- }
311
-
312
- /**
313
- * Run a function with a specific span set as active
314
- *
315
- * This is a convenience wrapper around the two-step process of
316
- * setting a span in context and running code within that context.
317
- *
318
- * @param span - The span to set as active
319
- * @param fn - Function to execute with the span active
320
- * @returns The return value of the function
321
- *
322
- * @example Running code with a custom span
323
- * ```typescript
324
- * import { getTracer, runWithSpan } from 'autotel';
325
- *
326
- * const tracer = getTracer('my-service');
327
- * const span = tracer.startSpan('background.job');
328
- *
329
- * try {
330
- * const result = await runWithSpan(span, async () => {
331
- * // Any spans created here will be children of 'background.job'
332
- * await processData();
333
- * return { success: true };
334
- * });
335
- * console.log(result);
336
- * } finally {
337
- * span.end();
338
- * }
339
- * ```
340
- *
341
- * @example Testing with custom spans
342
- * ```typescript
343
- * import { runWithSpan, otelTrace } from 'autotel';
344
- *
345
- * const tracer = otelTrace.getTracer('test');
346
- * const span = tracer.startSpan('test.operation');
347
- *
348
- * const result = runWithSpan(span, () => {
349
- * // Code under test runs with this span as active
350
- * return myFunction();
351
- * });
352
- *
353
- * span.end();
354
- * ```
355
- */
356
- export function runWithSpan<T>(span: Span, fn: () => T): T {
357
- const ctx = trace.setSpan(context.active(), span);
358
- return context.with(ctx, fn);
359
- }
360
-
361
- /**
362
- * Finalize a span with appropriate status and optional error recording
363
- *
364
- * This is a convenience function that:
365
- * - Records exceptions if an error is provided
366
- * - Sets span status to ERROR if error exists, OK otherwise
367
- * - Ends the span
368
- *
369
- * @param span - The span to finalize
370
- * @param error - Optional error to record
371
- *
372
- * @example Without error (success case)
373
- * ```typescript
374
- * import { getTracer, finalizeSpan } from 'autotel';
375
- *
376
- * const tracer = getTracer('my-service');
377
- * const span = tracer.startSpan('operation');
378
- *
379
- * try {
380
- * await doWork();
381
- * finalizeSpan(span);
382
- * } catch (error) {
383
- * finalizeSpan(span, error);
384
- * throw error;
385
- * }
386
- * ```
387
- *
388
- * @example With error
389
- * ```typescript
390
- * import { getTracer, finalizeSpan } from 'autotel';
391
- *
392
- * const tracer = getTracer('my-service');
393
- * const span = tracer.startSpan('operation');
394
- *
395
- * try {
396
- * await riskyOperation();
397
- * finalizeSpan(span);
398
- * } catch (error) {
399
- * finalizeSpan(span, error); // Records exception and sets ERROR status
400
- * throw error;
401
- * }
402
- * ```
403
- *
404
- * @example In instrumentation
405
- * ```typescript
406
- * import { getTracer, runWithSpan, finalizeSpan } from 'autotel';
407
- *
408
- * function instrumentedQuery(query: string) {
409
- * const tracer = getTracer('db');
410
- * const span = tracer.startSpan('db.query');
411
- *
412
- * return runWithSpan(span, () => {
413
- * try {
414
- * const result = executeQuery(query);
415
- * finalizeSpan(span);
416
- * return result;
417
- * } catch (error) {
418
- * finalizeSpan(span, error);
419
- * throw error;
420
- * }
421
- * });
422
- * }
423
- * ```
424
- */
425
- export function finalizeSpan(span: Span, error?: unknown): void {
426
- if (error) {
427
- if (error instanceof Error) {
428
- span.recordException(error);
429
- } else {
430
- span.recordException(new Error(String(error)));
431
- }
432
- span.setStatus({ code: SpanStatusCode.ERROR });
433
- } else {
434
- span.setStatus({ code: SpanStatusCode.OK });
435
- }
436
- span.end();
437
- }
438
-
439
- /**
440
- * Creates a deterministic trace ID from a seed string.
441
- *
442
- * Generates a consistent 128-bit trace ID (32 hex characters) from an input seed
443
- * using SHA-256 hashing. Useful for correlating external system IDs (request IDs,
444
- * order IDs, session IDs) with OpenTelemetry trace IDs.
445
- *
446
- * **Use Cases:**
447
- * - Correlate external request IDs with traces
448
- * - Link customer support tickets to trace data
449
- * - Associate business entities (orders, sessions) with observability data
450
- * - Debug specific user flows by deterministic trace lookup
451
- *
452
- * **Important:** Only use this when you need deterministic trace IDs for correlation.
453
- * For normal tracing, let OpenTelemetry generate random trace IDs automatically.
454
- *
455
- * **Runtime Support:**
456
- * - Node.js 15+ (native crypto.subtle)
457
- * - All modern browsers
458
- * - Edge runtimes (Cloudflare Workers, Deno, etc.)
459
- *
460
- * @param seed - Input string to generate trace ID from (e.g., request ID, order ID)
461
- * @returns Promise resolving to a 32-character hex trace ID (128 bits)
462
- *
463
- * @example Correlate external request ID with trace
464
- * ```typescript
465
- * import { createDeterministicTraceId } from 'autotel/trace-helpers'
466
- * import { trace, context } from '@opentelemetry/api'
467
- *
468
- * // In middleware or request handler
469
- * const requestId = req.headers['x-request-id']
470
- * const traceId = await createDeterministicTraceId(requestId)
471
- *
472
- * // Use with manual span creation (advanced - not needed with trace/span functions)
473
- * const tracer = trace.getTracer('my-service')
474
- * const spanContext = {
475
- * traceId,
476
- * spanId: '0123456789abcdef', // Still random
477
- * traceFlags: 1
478
- * }
479
- * ```
480
- *
481
- * @example Link customer support tickets to traces
482
- * ```typescript
483
- * import { createDeterministicTraceId } from 'autotel/trace-helpers'
484
- *
485
- * // Support dashboard integration
486
- * const ticketId = 'TICKET-12345'
487
- * const traceId = await createDeterministicTraceId(ticketId)
488
- *
489
- * // Generate direct link to traces in observability backend
490
- * const traceUrl = `https://your-otel-backend.com/traces/${traceId}`
491
- * console.log(`View related traces: ${traceUrl}`)
492
- * ```
493
- *
494
- * @example Session-based correlation
495
- * ```typescript
496
- * import { createDeterministicTraceId } from 'autotel/trace-helpers'
497
- *
498
- * // Track all operations for a user session
499
- * const sessionId = req.session.id
500
- * const traceId = await createDeterministicTraceId(sessionId)
501
- *
502
- * // All operations in this session share the same trace ID
503
- * // Makes it easy to find all activity for a specific session
504
- * ```
505
- *
506
- * @public
507
- */
508
- export async function createDeterministicTraceId(
509
- seed: string,
510
- ): Promise<string> {
511
- // Encode seed string to bytes
512
- const encoder = new TextEncoder();
513
- const data = encoder.encode(seed);
514
-
515
- // Generate SHA-256 hash (256 bits)
516
- const hashBuffer = await crypto.subtle.digest('SHA-256', data);
517
-
518
- // Convert to hex string and truncate to 32 characters (128 bits)
519
- // OpenTelemetry trace IDs are 128 bits (16 bytes, 32 hex characters)
520
- const hashArray = new Uint8Array(hashBuffer);
521
- return [...hashArray]
522
- .map((byte) => byte.toString(16).padStart(2, '0'))
523
- .join('')
524
- .slice(0, 32);
525
- }
526
-
527
- /**
528
- * Flattens nested metadata objects into dot-notation span attributes.
529
- *
530
- * Converts complex nested objects into flat key-value pairs suitable for
531
- * OpenTelemetry span attributes. Non-string values are JSON serialized.
532
- * Handles serialization failures gracefully with a fallback value.
533
- *
534
- * **Use Cases:**
535
- * - Structured metadata with nested objects
536
- * - User context with multiple properties
537
- * - Request/response metadata
538
- * - Business entity attributes
539
- *
540
- * **Note:** Filters out null/undefined values automatically to keep spans clean.
541
- *
542
- * @param metadata - Nested metadata object to flatten
543
- * @param prefix - Prefix for all attribute keys (default: 'metadata')
544
- * @returns Flattened attributes as { [key: string]: string }
545
- *
546
- * @example Basic metadata flattening
547
- * ```typescript
548
- * import { flattenMetadata } from 'autotel/trace-helpers'
549
- * import { trace } from 'autotel'
550
- *
551
- * export const processOrder = trace(ctx => async (orderId: string) => {
552
- * const order = await getOrder(orderId)
553
- *
554
- * // Flatten complex order metadata
555
- * const flattened = flattenMetadata({
556
- * user: { id: order.userId, tier: 'premium' },
557
- * payment: { method: 'card', processor: 'stripe' },
558
- * items: order.items.length
559
- * })
560
- *
561
- * ctx.setAttributes(flattened)
562
- * // Results in:
563
- * // {
564
- * // 'metadata.user.id': 'user-123',
565
- * // 'metadata.user.tier': 'premium',
566
- * // 'metadata.payment.method': 'card',
567
- * // 'metadata.payment.processor': 'stripe',
568
- * // 'metadata.items': '5'
569
- * // }
570
- * })
571
- * ```
572
- *
573
- * @example Custom prefix for semantic conventions
574
- * ```typescript
575
- * import { flattenMetadata } from 'autotel/trace-helpers'
576
- * import { trace } from 'autotel'
577
- *
578
- * export const fetchUser = trace(ctx => async (userId: string) => {
579
- * const user = await db.users.findOne({ id: userId })
580
- *
581
- * // Use semantic convention prefix
582
- * const userAttrs = flattenMetadata(
583
- * {
584
- * id: user.id,
585
- * email: user.email,
586
- * plan: user.subscription.plan
587
- * },
588
- * 'user' // Custom prefix
589
- * )
590
- *
591
- * ctx.setAttributes(userAttrs)
592
- * // Results in:
593
- * // {
594
- * // 'user.id': 'user-123',
595
- * // 'user.email': 'user@example.com',
596
- * // 'user.plan': 'enterprise'
597
- * // }
598
- * })
599
- * ```
600
- *
601
- * @example With complex objects (auto-serialized)
602
- * ```typescript
603
- * import { flattenMetadata } from 'autotel/trace-helpers'
604
- * import { trace } from 'autotel'
605
- *
606
- * export const analyzeRequest = trace(ctx => async (req: Request) => {
607
- * const metadata = flattenMetadata({
608
- * headers: req.headers, // Object - will be JSON serialized
609
- * query: req.query, // Object - will be JSON serialized
610
- * timestamp: new Date() // Non-string - will be JSON serialized
611
- * })
612
- *
613
- * ctx.setAttributes(metadata)
614
- * // Results in:
615
- * // {
616
- * // 'metadata.headers': '{"accept":"application/json",...}',
617
- * // 'metadata.query': '{"page":"1","limit":"10"}',
618
- * // 'metadata.timestamp': '"2024-01-15T12:00:00.000Z"'
619
- * // }
620
- * })
621
- * ```
622
- *
623
- * @example Error handling
624
- * ```typescript
625
- * import { flattenMetadata } from 'autotel/trace-helpers'
626
- *
627
- * // Objects with circular references are handled gracefully
628
- * const circular: any = { a: 1 }
629
- * circular.self = circular
630
- *
631
- * const flattened = flattenMetadata({ data: circular })
632
- * // Results in:
633
- * // { 'metadata.data': '<serialization-failed>' }
634
- * ```
635
- *
636
- * @public
637
- */
638
- /**
639
- * Resolve a trace URL from a template string and trace ID.
640
- *
641
- * Templates use `{traceId}` as placeholder. Falls back to `OTEL_TRACE_URL_TEMPLATE` env var.
642
- *
643
- * @example
644
- * resolveTraceUrl('https://grafana.example.com/explore?traceId={traceId}', 'abc123')
645
- * // => 'https://grafana.example.com/explore?traceId=abc123'
646
- */
647
- export function resolveTraceUrl(
648
- template: string | undefined,
649
- traceId: string,
650
- ): string | undefined {
651
- const t = template ?? process.env.OTEL_TRACE_URL_TEMPLATE;
652
- if (!t) return undefined;
653
- return t.replace(/\{traceId\}/g, traceId);
654
- }
655
-
656
- export function flattenMetadata(
657
- metadata: Record<string, unknown>,
658
- prefix = 'metadata',
659
- ): Record<string, string> {
660
- const flattened: Record<string, string> = {};
661
- const seen = new WeakSet<object>(); // Track visited objects to detect cycles
662
-
663
- function flatten(obj: Record<string, unknown>, currentPrefix: string): void {
664
- for (const [key, value] of Object.entries(obj)) {
665
- // Skip null/undefined values
666
- if (value == null) continue;
667
-
668
- const attributeKey = `${currentPrefix}.${key}`;
669
-
670
- // Handle primitives directly (string, number, boolean)
671
- if (typeof value === 'string') {
672
- flattened[attributeKey] = value;
673
- continue;
674
- }
675
- if (typeof value === 'number' || typeof value === 'boolean') {
676
- flattened[attributeKey] = String(value);
677
- continue;
678
- }
679
-
680
- // Recursively flatten plain objects (with cycle detection)
681
- if (
682
- typeof value === 'object' &&
683
- value !== null &&
684
- value.constructor === Object
685
- ) {
686
- // Detect circular references
687
- if (seen.has(value)) {
688
- flattened[attributeKey] = '<circular-reference>';
689
- continue;
690
- }
691
-
692
- // Mark as visited and recursively flatten
693
- seen.add(value);
694
- flatten(value as Record<string, unknown>, attributeKey);
695
- continue;
696
- }
697
-
698
- // Serialize arrays and other non-plain objects to JSON
699
- try {
700
- flattened[attributeKey] = JSON.stringify(value);
701
- } catch {
702
- // Handle circular references or non-serializable objects
703
- flattened[attributeKey] = '<serialization-failed>';
704
- }
705
- }
706
- }
707
-
708
- flatten(metadata, prefix);
709
- return flattened;
710
- }