autotel 4.1.0 → 4.2.0
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.
- package/package.json +1 -2
- package/src/attribute-redacting-processor.test.ts +0 -763
- package/src/attribute-redacting-processor.ts +0 -621
- package/src/attributes/attachers.ts +0 -161
- package/src/attributes/builders.ts +0 -529
- package/src/attributes/domains.ts +0 -42
- package/src/attributes/index.ts +0 -81
- package/src/attributes/registry.ts +0 -323
- package/src/attributes/types.ts +0 -211
- package/src/attributes/utils.ts +0 -64
- package/src/attributes/validators.ts +0 -266
- package/src/attributes.test.ts +0 -292
- package/src/auto.ts +0 -67
- package/src/autotel-logger.test.ts +0 -548
- package/src/autotel-logger.ts +0 -364
- package/src/baggage-span-processor.test.ts +0 -202
- package/src/baggage-span-processor.ts +0 -100
- package/src/business-baggage.test.ts +0 -500
- package/src/business-baggage.ts +0 -669
- package/src/circuit-breaker.test.ts +0 -341
- package/src/circuit-breaker.ts +0 -184
- package/src/config.test.ts +0 -94
- package/src/config.ts +0 -172
- package/src/correlated-events.test.ts +0 -151
- package/src/correlated-events.ts +0 -47
- package/src/correlation-id.test.ts +0 -163
- package/src/correlation-id.ts +0 -206
- package/src/db.test.ts +0 -252
- package/src/db.ts +0 -447
- package/src/decorators.test.ts +0 -153
- package/src/decorators.ts +0 -188
- package/src/define-event.test.ts +0 -41
- package/src/define-event.ts +0 -58
- package/src/devtools.ts +0 -60
- package/src/drain-pipeline.test.ts +0 -68
- package/src/drain-pipeline.ts +0 -199
- package/src/drain-toolkit.test.ts +0 -113
- package/src/drain-toolkit.ts +0 -129
- package/src/enricher-toolkit.test.ts +0 -67
- package/src/enricher-toolkit.ts +0 -79
- package/src/enrichers.test.ts +0 -150
- package/src/enrichers.ts +0 -145
- package/src/env-config.test.ts +0 -323
- package/src/env-config.ts +0 -309
- package/src/error-catalog.test.ts +0 -133
- package/src/error-catalog.ts +0 -262
- package/src/event-queue.test.ts +0 -864
- package/src/event-queue.ts +0 -699
- package/src/event-subscriber.ts +0 -262
- package/src/event-testing.ts +0 -197
- package/src/event.test.ts +0 -1104
- package/src/event.ts +0 -988
- package/src/events-config.ts +0 -235
- package/src/exporters.ts +0 -165
- package/src/filtering-span-processor.test.ts +0 -281
- package/src/filtering-span-processor.ts +0 -111
- package/src/flatten-attributes.test.ts +0 -76
- package/src/flatten-attributes.ts +0 -80
- package/src/functional.strict-types.typecheck.ts +0 -53
- package/src/functional.test.ts +0 -1464
- package/src/functional.ts +0 -2539
- package/src/functional.types.test.ts +0 -135
- package/src/hook.mjs +0 -15
- package/src/http.test.ts +0 -485
- package/src/http.ts +0 -424
- package/src/index.ts +0 -433
- package/src/init-auto-redactor.test.ts +0 -53
- package/src/init-redactor.test.ts +0 -8
- package/src/init.customization.test.ts +0 -665
- package/src/init.integrations.test.ts +0 -399
- package/src/init.openllmetry.test.ts +0 -194
- package/src/init.protocol.test.ts +0 -215
- package/src/init.ts +0 -2439
- package/src/instrumentation.test.ts +0 -108
- package/src/instrumentation.ts +0 -319
- package/src/logger.test.ts +0 -125
- package/src/logger.ts +0 -341
- package/src/messaging-adapters.test.ts +0 -595
- package/src/messaging-adapters.ts +0 -583
- package/src/messaging-testing.test.ts +0 -573
- package/src/messaging-testing.ts +0 -935
- package/src/messaging.test.ts +0 -1646
- package/src/messaging.ts +0 -2245
- package/src/metric-helpers.ts +0 -47
- package/src/metric-testing.ts +0 -197
- package/src/metric.ts +0 -446
- package/src/metrics.test.ts +0 -241
- package/src/node-require.ts +0 -123
- package/src/operation-context.ts +0 -93
- package/src/parse-error.test.ts +0 -73
- package/src/parse-error.ts +0 -112
- package/src/posthog-logs.test.ts +0 -115
- package/src/posthog-logs.ts +0 -77
- package/src/pretty-console-exporter.test.ts +0 -545
- package/src/pretty-console-exporter.ts +0 -413
- package/src/pretty-log-formatter.test.ts +0 -123
- package/src/pretty-log-formatter.ts +0 -210
- package/src/processors/canonical-log-line-processor.test.ts +0 -523
- package/src/processors/canonical-log-line-processor.ts +0 -396
- package/src/processors.ts +0 -152
- package/src/rate-limiter.test.ts +0 -199
- package/src/rate-limiter.ts +0 -98
- package/src/redact-values.test.ts +0 -90
- package/src/redact-values.ts +0 -34
- package/src/register.ts +0 -37
- package/src/request-logger.test.ts +0 -545
- package/src/request-logger.ts +0 -342
- package/src/sampling.test.ts +0 -1060
- package/src/sampling.ts +0 -737
- package/src/security-schema.test.ts +0 -45
- package/src/security-schema.ts +0 -107
- package/src/semantic-conventions.ts +0 -15
- package/src/semantic-helpers.test.ts +0 -226
- package/src/semantic-helpers.ts +0 -438
- package/src/shutdown.test.ts +0 -364
- package/src/shutdown.ts +0 -246
- package/src/span-name-normalizer.test.ts +0 -377
- package/src/span-name-normalizer.ts +0 -213
- package/src/stable-hash.ts +0 -27
- package/src/structured-error.test.ts +0 -191
- package/src/structured-error.ts +0 -157
- package/src/stub.integration.test.ts +0 -361
- package/src/tail-sampling-processor.test.ts +0 -230
- package/src/tail-sampling-processor.ts +0 -55
- package/src/test-span-collector.test.ts +0 -234
- package/src/test-span-collector.ts +0 -150
- package/src/testing.ts +0 -705
- package/src/trace-context.test.ts +0 -73
- package/src/trace-context.ts +0 -567
- package/src/trace-helpers.new.test.ts +0 -278
- package/src/trace-helpers.test.ts +0 -290
- package/src/trace-helpers.ts +0 -710
- package/src/trace-hybrid.test.ts +0 -42
- package/src/trace-hybrid.ts +0 -37
- package/src/tracer-provider.test.ts +0 -183
- package/src/tracer-provider.ts +0 -266
- package/src/track.test.ts +0 -154
- package/src/track.ts +0 -216
- package/src/validate.test.ts +0 -287
- package/src/validate.ts +0 -307
- package/src/validation-attributes.ts +0 -43
- package/src/validation.test.ts +0 -330
- package/src/validation.ts +0 -246
- package/src/variable-name-inference.test.ts +0 -178
- package/src/variable-name-inference.ts +0 -242
- package/src/webhook.test.ts +0 -649
- package/src/webhook.ts +0 -637
- package/src/workflow-distributed.test.ts +0 -786
- package/src/workflow-distributed.ts +0 -916
- package/src/workflow.async-safety.integration.test.ts +0 -345
- package/src/workflow.test.ts +0 -647
- package/src/workflow.ts +0 -810
- package/src/yaml-config.test.ts +0 -373
- 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;
|
|
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';
|