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,396 +0,0 @@
1
- /**
2
- * Canonical Log Line Processor
3
- *
4
- * Automatically emits spans as canonical log lines (wide events) when they end.
5
- * Implements canonical log line" pattern: one comprehensive
6
- * event per request with all context.
7
- *
8
- * When a span ends, this processor creates a log record with ALL span attributes,
9
- * making the span itself the canonical log line that can be queried like structured data.
10
- *
11
- * @example
12
- * ```typescript
13
- * import { init } from 'autotel';
14
- *
15
- * init({
16
- * service: 'my-app',
17
- * canonicalLogLines: {
18
- * enabled: true,
19
- * rootSpansOnly: true, // One canonical log line per request
20
- * },
21
- * });
22
- * ```
23
- */
24
-
25
- import type {
26
- SpanProcessor,
27
- ReadableSpan,
28
- } from '@opentelemetry/sdk-trace-base';
29
- import type { Attributes, AttributeValue } from '@opentelemetry/api';
30
- import { logs, SeverityNumber } from '@opentelemetry/api-logs';
31
- import type { Logger } from '../logger';
32
- import { formatPrettyLogLine, formatDuration } from '../pretty-log-formatter';
33
-
34
- /**
35
- * Function to redact sensitive attribute values
36
- */
37
- export type AttributeRedactorFn = (
38
- key: string,
39
- value: AttributeValue,
40
- ) => AttributeValue;
41
-
42
- export interface CanonicalLogLineEvent {
43
- span: ReadableSpan;
44
- level: 'debug' | 'info' | 'warn' | 'error';
45
- message: string;
46
- event: Record<string, unknown>;
47
- }
48
-
49
- export interface KeepCondition {
50
- /** Keep events where HTTP status >= this value. */
51
- status?: number;
52
- /** Keep events where duration_ms >= this value. */
53
- durationMs?: number;
54
- /** Keep events matching this path pattern (simple prefix match). */
55
- path?: string;
56
- }
57
-
58
- export interface CanonicalLogLineOptions {
59
- /** Logger to use for emitting canonical log lines (defaults to OTel Logs API) */
60
- logger?: Logger;
61
- /** Only emit canonical log lines for root spans (default: false) */
62
- rootSpansOnly?: boolean;
63
- /** Minimum log level for canonical log lines (default: 'info') */
64
- minLevel?: 'debug' | 'info' | 'warn' | 'error';
65
- /** Custom message format (default: uses span name) */
66
- messageFormat?: (span: ReadableSpan) => string;
67
- /** Whether to include resource attributes (default: true) */
68
- includeResourceAttributes?: boolean;
69
- /**
70
- * Attribute redactor function to apply before logging.
71
- * This ensures sensitive data is redacted in canonical log lines,
72
- * matching the behavior of attributeRedactor in init().
73
- */
74
- attributeRedactor?: AttributeRedactorFn;
75
- /** Predicate to decide whether to emit (runs after event is built). */
76
- shouldEmit?: (ctx: CanonicalLogLineEvent) => boolean;
77
- /**
78
- * Declarative tail sampling conditions (OR logic). If any condition matches,
79
- * the event is kept. Ignored when `shouldEmit` is provided.
80
- *
81
- * @example
82
- * keep: [{ status: 500 }, { durationMs: 1000 }]
83
- */
84
- keep?: KeepCondition[];
85
- /** Callback invoked after emit for custom fan-out. */
86
- drain?: (ctx: CanonicalLogLineEvent) => void | Promise<void>;
87
- /** Handler for drain failures. */
88
- onDrainError?: (error: unknown, ctx: CanonicalLogLineEvent) => void;
89
- /**
90
- * Pretty-print canonical log lines to console in a tree format.
91
- * Defaults to true when NODE_ENV is 'development'.
92
- */
93
- pretty?: boolean;
94
- }
95
-
96
- /**
97
- * Span processor that automatically emits spans as canonical log lines
98
- *
99
- * When a span ends, this processor creates a log record with ALL span attributes.
100
- * This implements the "canonical log line" pattern: one comprehensive event
101
- * per request with all context, queryable as structured data.
102
- *
103
- * **Key Benefits:**
104
- * - One log line per request with all context (wide event)
105
- * - High-cardinality, high-dimensionality data for powerful queries
106
- * - Automatic - no manual logging needed
107
- * - Works with any logger or OTel Logs API
108
- *
109
- * @example Basic usage
110
- * ```typescript
111
- * import { init } from 'autotel';
112
- *
113
- * init({
114
- * service: 'checkout-api',
115
- * canonicalLogLines: {
116
- * enabled: true,
117
- * rootSpansOnly: true, // One canonical log line per request
118
- * },
119
- * });
120
- * ```
121
- *
122
- * @example With custom logger
123
- * ```typescript
124
- * import pino from 'pino';
125
- * import { init } from 'autotel';
126
- *
127
- * const logger = pino();
128
- * init({
129
- * service: 'my-app',
130
- * logger,
131
- * canonicalLogLines: {
132
- * enabled: true,
133
- * logger, // Use Pino for canonical log lines
134
- * rootSpansOnly: true,
135
- * },
136
- * });
137
- * ```
138
- *
139
- * @example Custom message format
140
- * ```typescript
141
- * init({
142
- * service: 'my-app',
143
- * canonicalLogLines: {
144
- * enabled: true,
145
- * messageFormat: (span) => {
146
- * const status = span.status.code === 2 ? 'ERROR' : 'SUCCESS';
147
- * return `${span.name} [${status}]`;
148
- * },
149
- * },
150
- * });
151
- * ```
152
- */
153
- export class CanonicalLogLineProcessor implements SpanProcessor {
154
- private logger?: Logger;
155
- private rootSpansOnly: boolean;
156
- private minLevel: 'debug' | 'info' | 'warn' | 'error';
157
- private messageFormat: (span: ReadableSpan) => string;
158
- private includeResourceAttributes: boolean;
159
- private attributeRedactor?: AttributeRedactorFn;
160
- private shouldEmit?: (ctx: CanonicalLogLineEvent) => boolean;
161
- private drain?: (ctx: CanonicalLogLineEvent) => void | Promise<void>;
162
- private onDrainError?: (error: unknown, ctx: CanonicalLogLineEvent) => void;
163
- private pretty: boolean;
164
- private getOTelLogger: (() => ReturnType<typeof logs.getLogger>) | null =
165
- null;
166
-
167
- constructor(options: CanonicalLogLineOptions = {}) {
168
- this.logger = options.logger;
169
- this.rootSpansOnly = options.rootSpansOnly ?? false;
170
- this.minLevel = options.minLevel ?? 'info';
171
- this.messageFormat =
172
- options.messageFormat ?? ((span) => `[${span.name}] Request completed`);
173
- this.includeResourceAttributes = options.includeResourceAttributes ?? true;
174
- this.attributeRedactor = options.attributeRedactor;
175
- this.shouldEmit =
176
- options.shouldEmit ?? this.buildKeepPredicate(options.keep);
177
- this.drain = options.drain;
178
- this.onDrainError = options.onDrainError;
179
- this.pretty =
180
- options.pretty ??
181
- (typeof process !== 'undefined' &&
182
- process.env.NODE_ENV === 'development');
183
-
184
- if (!this.logger) {
185
- this.getOTelLogger = () => logs.getLogger('autotel.canonical-log-line');
186
- }
187
- }
188
-
189
- private buildKeepPredicate(
190
- keep?: KeepCondition[],
191
- ): ((ctx: CanonicalLogLineEvent) => boolean) | undefined {
192
- if (!keep || keep.length === 0) return undefined;
193
-
194
- return (ctx: CanonicalLogLineEvent) => {
195
- return keep.some((condition) => {
196
- if (condition.status !== undefined) {
197
- const httpStatus = Number(
198
- ctx.event['http.response.status_code'] ?? 0,
199
- );
200
- if (httpStatus >= condition.status) return true;
201
- }
202
- if (
203
- condition.durationMs !== undefined &&
204
- Number(ctx.event.duration_ms ?? 0) >= condition.durationMs
205
- ) {
206
- return true;
207
- }
208
- if (condition.path !== undefined) {
209
- const route = String(
210
- ctx.event['http.route'] ?? ctx.event['url.path'] ?? '',
211
- );
212
- if (route.startsWith(condition.path)) return true;
213
- }
214
- return false;
215
- });
216
- };
217
- }
218
-
219
- onStart(): void {
220
- // No-op
221
- }
222
-
223
- onEnd(span: ReadableSpan): void {
224
- if (
225
- this.rootSpansOnly &&
226
- span.parentSpanContext?.spanId &&
227
- !span.parentSpanContext.isRemote
228
- ) {
229
- return;
230
- }
231
-
232
- const level = this.getLogLevel(span);
233
- if (!this.shouldLog(level)) {
234
- return;
235
- }
236
-
237
- const canonicalLogLine = this.buildCanonicalLogLine(span);
238
- const message = this.messageFormat(span);
239
- const eventContext: CanonicalLogLineEvent = {
240
- span,
241
- level,
242
- message,
243
- event: canonicalLogLine,
244
- };
245
-
246
- if (this.shouldEmit && !this.shouldEmit(eventContext)) return;
247
-
248
- if (this.pretty) {
249
- console.log(formatPrettyLogLine(eventContext));
250
- }
251
-
252
- if (this.logger) {
253
- this.emitViaLogger(level, message, canonicalLogLine);
254
- } else if (this.getOTelLogger) {
255
- const otelLogger = this.getOTelLogger();
256
- this.emitViaOTel(level, message, canonicalLogLine, otelLogger);
257
- }
258
-
259
- if (this.drain) {
260
- Promise.resolve(this.drain(eventContext)).catch((error) => {
261
- if (this.onDrainError) {
262
- this.onDrainError(error, eventContext);
263
- return;
264
- }
265
- this.reportInternalWarning('canonicalLogLines.drain failed', error);
266
- });
267
- }
268
- }
269
-
270
- private buildCanonicalLogLine(span: ReadableSpan): Record<string, unknown> {
271
- const durationMs = span.duration[0] * 1000 + span.duration[1] / 1_000_000;
272
- const timestamp = new Date(
273
- span.startTime[0] * 1000 + span.startTime[1] / 1_000_000,
274
- ).toISOString();
275
-
276
- // Span attributes first so core metadata fields below take precedence
277
- const canonicalLogLine: Record<string, unknown> = {};
278
- const attributes = this.redactAttributes(span.attributes);
279
- Object.assign(canonicalLogLine, attributes);
280
-
281
- if (this.includeResourceAttributes) {
282
- const resourceAttrs = this.redactAttributes(
283
- span.resource.attributes as Attributes,
284
- );
285
- Object.assign(canonicalLogLine, resourceAttrs);
286
- }
287
-
288
- canonicalLogLine.operation = span.name;
289
- canonicalLogLine.traceId = span.spanContext().traceId;
290
- canonicalLogLine.spanId = span.spanContext().spanId;
291
- canonicalLogLine.correlationId = span.spanContext().traceId.slice(0, 16);
292
- canonicalLogLine.duration_ms = Math.round(durationMs * 100) / 100;
293
- canonicalLogLine.duration = formatDuration(durationMs);
294
- canonicalLogLine.status_code = span.status.code;
295
- canonicalLogLine.status_message = span.status.message || undefined;
296
- canonicalLogLine.timestamp = timestamp;
297
-
298
- return canonicalLogLine;
299
- }
300
-
301
- private redactAttributes(attributes: Attributes): Record<string, unknown> {
302
- if (!this.attributeRedactor) {
303
- return { ...attributes };
304
- }
305
-
306
- const redacted: Record<string, unknown> = {};
307
- for (const [key, value] of Object.entries(attributes)) {
308
- if (value !== undefined) {
309
- redacted[key] = this.attributeRedactor(key, value);
310
- }
311
- }
312
- return redacted;
313
- }
314
-
315
- private emitViaLogger(
316
- level: 'debug' | 'info' | 'warn' | 'error',
317
- message: string,
318
- canonicalLogLine: Record<string, unknown>,
319
- ): void {
320
- this.logger![level](canonicalLogLine, message);
321
- }
322
-
323
- private emitViaOTel(
324
- level: 'debug' | 'info' | 'warn' | 'error',
325
- message: string,
326
- canonicalLogLine: Record<string, unknown>,
327
- otelLogger: ReturnType<typeof logs.getLogger>,
328
- ): void {
329
- const otelAttributes: Record<string, string | number | boolean> = {};
330
- for (const [key, value] of Object.entries(canonicalLogLine)) {
331
- if (
332
- typeof value === 'string' ||
333
- typeof value === 'number' ||
334
- typeof value === 'boolean'
335
- ) {
336
- otelAttributes[key] = value;
337
- } else if (value !== null && value !== undefined) {
338
- otelAttributes[key] = String(value);
339
- }
340
- }
341
- otelLogger.emit({
342
- severityNumber: this.getSeverityNumber(level),
343
- severityText: level.toUpperCase(),
344
- body: message,
345
- attributes: otelAttributes,
346
- });
347
- }
348
-
349
- private getLogLevel(span: ReadableSpan): 'debug' | 'info' | 'warn' | 'error' {
350
- const explicitLevel = span.attributes['autotel.log.level'];
351
- if (
352
- explicitLevel === 'debug' ||
353
- explicitLevel === 'info' ||
354
- explicitLevel === 'warn' ||
355
- explicitLevel === 'error'
356
- ) {
357
- return explicitLevel;
358
- }
359
-
360
- if (span.status.code === 2) return 'error';
361
- return 'info';
362
- }
363
-
364
- private shouldLog(level: string): boolean {
365
- const levels = ['debug', 'info', 'warn', 'error'];
366
- return levels.indexOf(level) >= levels.indexOf(this.minLevel);
367
- }
368
-
369
- private getSeverityNumber(level: string): SeverityNumber {
370
- const mapping: Record<string, SeverityNumber> = {
371
- debug: SeverityNumber.DEBUG,
372
- info: SeverityNumber.INFO,
373
- warn: SeverityNumber.WARN,
374
- error: SeverityNumber.ERROR,
375
- };
376
- return mapping[level] ?? SeverityNumber.INFO;
377
- }
378
-
379
- private reportInternalWarning(message: string, error: unknown): void {
380
- const err =
381
- error instanceof Error ? error.message : String(error ?? 'unknown error');
382
- if (this.logger) {
383
- this.logger.warn({ error: err }, `[autotel] ${message}`);
384
- return;
385
- }
386
- console.warn(`[autotel] ${message}: ${err}`);
387
- }
388
-
389
- async forceFlush(): Promise<void> {
390
- // No-op
391
- }
392
-
393
- async shutdown(): Promise<void> {
394
- // No-op
395
- }
396
- }
package/src/processors.ts DELETED
@@ -1,152 +0,0 @@
1
- /**
2
- * OpenTelemetry Span Processors
3
- *
4
- * Re-exports commonly-needed OpenTelemetry span processors for custom configurations.
5
- *
6
- * These processors are already included in autotel's dependencies, so re-exporting
7
- * them provides a "one install is all you need" developer experience without any
8
- * bundle size impact.
9
- *
10
- * Use these when you need custom span processing logic beyond what `init()` provides.
11
- *
12
- * @example Simple processor (synchronous, for testing)
13
- * ```typescript
14
- * import { init } from 'autotel'
15
- * import { InMemorySpanExporter } from 'autotel/exporters'
16
- * import { SimpleSpanProcessor } from 'autotel/processors'
17
- *
18
- * const exporter = new InMemorySpanExporter()
19
- * init({
20
- * service: 'test',
21
- * spanProcessor: new SimpleSpanProcessor(exporter),
22
- * })
23
- * ```
24
- *
25
- * @example Batch processor (async batching, for production)
26
- * ```typescript
27
- * import { init } from 'autotel'
28
- * import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
29
- * import { BatchSpanProcessor } from 'autotel/processors'
30
- *
31
- * const exporter = new OTLPTraceExporter({ url: 'http://collector:4318/v1/traces' })
32
- * init({
33
- * service: 'my-app',
34
- * spanProcessor: new BatchSpanProcessor(exporter, {
35
- * maxQueueSize: 2048,
36
- * scheduledDelayMillis: 5000,
37
- * exportTimeoutMillis: 30000,
38
- * maxExportBatchSize: 512,
39
- * }),
40
- * })
41
- * ```
42
- *
43
- * Note: Most users don't need to use processors directly - `init()` configures
44
- * BatchSpanProcessor by default. Use these when you need custom processing logic.
45
- *
46
- * @module autotel/processors
47
- * @see {@link https://opentelemetry.io/docs/specs/otel/trace/sdk/#span-processor | OTel Span Processor Spec}
48
- */
49
-
50
- export {
51
- /**
52
- * Simple span processor - processes spans synchronously.
53
- *
54
- * Perfect for:
55
- * - Unit testing (synchronous span export)
56
- * - Development (immediate span visibility)
57
- * - Debugging (no batching delays)
58
- *
59
- * How it works:
60
- * - Spans are exported immediately when they end
61
- * - No batching or queuing
62
- * - Blocking (export happens on the same thread)
63
- *
64
- * Warning: Not recommended for production - use BatchSpanProcessor instead.
65
- * SimpleSpanProcessor can impact performance in high-throughput scenarios.
66
- *
67
- * @example
68
- * ```typescript
69
- * import { SimpleSpanProcessor } from 'autotel/processors'
70
- * import { ConsoleSpanExporter } from 'autotel/exporters'
71
- *
72
- * const processor = new SimpleSpanProcessor(new ConsoleSpanExporter())
73
- * ```
74
- */
75
- SimpleSpanProcessor,
76
-
77
- /**
78
- * Batch span processor - batches spans before exporting.
79
- *
80
- * Perfect for:
81
- * - Production use (efficient, non-blocking)
82
- * - High-throughput applications
83
- * - Custom export configurations
84
- *
85
- * How it works:
86
- * - Spans are queued in memory
87
- * - Exported in batches at regular intervals
88
- * - Non-blocking (export happens on background thread)
89
- * - Configurable batch size, delay, queue size
90
- *
91
- * This is the default processor used by `init()`.
92
- *
93
- * @example Custom configuration
94
- * ```typescript
95
- * import { BatchSpanProcessor } from 'autotel/processors'
96
- *
97
- * const processor = new BatchSpanProcessor(exporter, {
98
- * maxQueueSize: 4096, // Max spans in queue
99
- * scheduledDelayMillis: 10000, // Export every 10s
100
- * exportTimeoutMillis: 30000, // 30s export timeout
101
- * maxExportBatchSize: 1024, // Max 1024 spans per batch
102
- * })
103
- * ```
104
- */
105
- BatchSpanProcessor,
106
- } from '@opentelemetry/sdk-trace-base';
107
-
108
- export {
109
- /**
110
- * Canonical log line processor - automatically emits spans as wide events
111
- *
112
- * When a span ends, this processor creates a log record with ALL span attributes.
113
- * This implements the "canonical log line" pattern: one comprehensive event
114
- * per request with all context, queryable as structured data.
115
- *
116
- * **Key Benefits:**
117
- * - One log line per request with all context (wide event)
118
- * - High-cardinality, high-dimensionality data for powerful queries
119
- * - Automatic - no manual logging needed
120
- * - Works with any logger or OTel Logs API
121
- *
122
- * @example Basic usage
123
- * ```typescript
124
- * import { init } from 'autotel';
125
- *
126
- * init({
127
- * service: 'checkout-api',
128
- * canonicalLogLines: {
129
- * enabled: true,
130
- * rootSpansOnly: true, // One canonical log line per request
131
- * },
132
- * });
133
- * ```
134
- *
135
- * @example With custom logger
136
- * ```typescript
137
- * import pino from 'pino';
138
- * const logger = pino();
139
- * init({
140
- * service: 'my-app',
141
- * logger,
142
- * canonicalLogLines: {
143
- * enabled: true,
144
- * logger, // Use Pino for canonical log lines
145
- * rootSpansOnly: true,
146
- * },
147
- * });
148
- * ```
149
- */
150
- CanonicalLogLineProcessor,
151
- type CanonicalLogLineOptions,
152
- } from './processors/canonical-log-line-processor';