autotel 4.0.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/README.md +26 -1
- package/dist/auto.cjs +2 -2
- package/dist/auto.js +1 -1
- package/dist/correlation-id.cjs +1 -1
- package/dist/correlation-id.js +1 -1
- package/dist/decorators.cjs +1 -1
- package/dist/decorators.js +1 -1
- package/dist/{event-Dlqr4ZNL.cjs → event-BhHREDJk.cjs} +3 -3
- package/dist/{event-Dlqr4ZNL.cjs.map → event-BhHREDJk.cjs.map} +1 -1
- package/dist/{event-_58ryBjh.js → event-ByBTV9M2.js} +3 -3
- package/dist/{event-_58ryBjh.js.map → event-ByBTV9M2.js.map} +1 -1
- package/dist/event.cjs +1 -1
- package/dist/event.js +1 -1
- package/dist/{functional-BGkT8J-h.js → functional-DtI0u4vx.js} +19 -19
- package/dist/functional-DtI0u4vx.js.map +1 -0
- package/dist/{functional-C4CzoVrX.cjs → functional-zpzNLhky.cjs} +4 -4
- package/dist/{functional-C4CzoVrX.cjs.map → functional-zpzNLhky.cjs.map} +1 -1
- package/dist/functional.cjs +1 -1
- package/dist/functional.js +1 -1
- package/dist/http.cjs +1 -1
- package/dist/http.js +1 -1
- package/dist/index.cjs +5 -5
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5 -5
- package/dist/{init-DJQOdVlN.d.ts → init-B7u-DjxM.d.ts} +57 -2
- package/dist/init-B7u-DjxM.d.ts.map +1 -0
- package/dist/{init-DvapOXCc.cjs → init-BX7AmFRl.cjs} +40 -21
- package/dist/init-BX7AmFRl.cjs.map +1 -0
- package/dist/{init-Ch6t7MNI.js → init-D-jnNMix.js} +39 -20
- package/dist/init-D-jnNMix.js.map +1 -0
- package/dist/{init-CNp-ee80.d.cts → init-DSrRmVnz.d.cts} +57 -2
- package/dist/init-DSrRmVnz.d.cts.map +1 -0
- package/dist/instrumentation.cjs +1 -1
- package/dist/instrumentation.js +1 -1
- package/dist/logger-D3Ej3DII.js +446 -0
- package/dist/logger-D3Ej3DII.js.map +1 -0
- package/dist/logger-thMPLpOG.cjs +487 -0
- package/dist/logger-thMPLpOG.cjs.map +1 -0
- package/dist/logger.cjs +8 -236
- package/dist/logger.js +2 -204
- package/dist/messaging.cjs +1 -1
- package/dist/messaging.js +1 -1
- package/dist/semantic-helpers.cjs +1 -1
- package/dist/semantic-helpers.js +1 -1
- package/dist/{track-3HY4NGV-.cjs → track-D59FfpL0.cjs} +2 -2
- package/dist/{track-3HY4NGV-.cjs.map → track-D59FfpL0.cjs.map} +1 -1
- package/dist/{track-nsKVy-pj.js → track-wc0HafS_.js} +6 -6
- package/dist/track-wc0HafS_.js.map +1 -0
- package/dist/webhook.cjs +1 -1
- package/dist/webhook.js +1 -1
- package/dist/workflow-distributed.cjs +1 -1
- package/dist/workflow-distributed.js +1 -1
- package/dist/workflow.cjs +1 -1
- package/dist/workflow.js +1 -1
- package/dist/{yaml-config-B3dQ82GR.cjs → yaml-config-Ck2uB0Dp.cjs} +2 -1
- package/dist/yaml-config-Ck2uB0Dp.cjs.map +1 -0
- package/dist/yaml-config.cjs +1 -1
- package/dist/yaml-config.d.cts +7 -1
- package/dist/yaml-config.d.cts.map +1 -1
- package/dist/yaml-config.d.ts +7 -1
- package/dist/yaml-config.d.ts.map +1 -1
- package/dist/yaml-config.js +1 -0
- package/dist/yaml-config.js.map +1 -1
- package/package.json +1 -2
- package/skills/autotel-core/SKILL.md +2 -0
- package/skills/autotel-instrumentation/SKILL.md +25 -0
- package/skills/debug-missing-spans/SKILL.md +3 -1
- package/skills/migrate-to-autotel/SKILL.md +24 -23
- package/skills/review-otel-patterns/SKILL.md +5 -4
- package/dist/functional-BGkT8J-h.js.map +0 -1
- package/dist/init-CNp-ee80.d.cts.map +0 -1
- package/dist/init-Ch6t7MNI.js.map +0 -1
- package/dist/init-DJQOdVlN.d.ts.map +0 -1
- package/dist/init-DvapOXCc.cjs.map +0 -1
- package/dist/logger.cjs.map +0 -1
- package/dist/logger.js.map +0 -1
- package/dist/track-nsKVy-pj.js.map +0 -1
- package/dist/yaml-config-B3dQ82GR.cjs.map +0 -1
- 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 -594
- 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 -2312
- 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 -337
- package/src/yaml-config.ts +0 -342
|
@@ -1,523 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
import { CanonicalLogLineProcessor } from './canonical-log-line-processor';
|
|
3
|
-
import type { ReadableSpan } from '@opentelemetry/sdk-trace-base';
|
|
4
|
-
import { SpanKind, SpanStatusCode } from '@opentelemetry/api';
|
|
5
|
-
import { resourceFromAttributes } from '@opentelemetry/resources';
|
|
6
|
-
import { logs } from '@opentelemetry/api-logs';
|
|
7
|
-
import type { Logger } from '../logger';
|
|
8
|
-
|
|
9
|
-
describe('CanonicalLogLineProcessor', () => {
|
|
10
|
-
let mockLogger: Logger;
|
|
11
|
-
let mockOTelLogger: ReturnType<typeof logs.getLogger>;
|
|
12
|
-
let logEntries: Array<{
|
|
13
|
-
level: string;
|
|
14
|
-
message: string;
|
|
15
|
-
attrs: Record<string, unknown>;
|
|
16
|
-
}>;
|
|
17
|
-
|
|
18
|
-
beforeEach(() => {
|
|
19
|
-
logEntries = [];
|
|
20
|
-
mockLogger = {
|
|
21
|
-
info: vi.fn((extra, msg) => {
|
|
22
|
-
logEntries.push({
|
|
23
|
-
level: 'info',
|
|
24
|
-
message: msg || '',
|
|
25
|
-
attrs: extra || {},
|
|
26
|
-
});
|
|
27
|
-
}),
|
|
28
|
-
warn: vi.fn((extra, msg) => {
|
|
29
|
-
logEntries.push({
|
|
30
|
-
level: 'warn',
|
|
31
|
-
message: msg || '',
|
|
32
|
-
attrs: extra || {},
|
|
33
|
-
});
|
|
34
|
-
}),
|
|
35
|
-
error: vi.fn((extra, msg) => {
|
|
36
|
-
logEntries.push({
|
|
37
|
-
level: 'error',
|
|
38
|
-
message: msg || '',
|
|
39
|
-
attrs: extra || {},
|
|
40
|
-
});
|
|
41
|
-
}),
|
|
42
|
-
debug: vi.fn((extra, msg) => {
|
|
43
|
-
logEntries.push({
|
|
44
|
-
level: 'debug',
|
|
45
|
-
message: msg || '',
|
|
46
|
-
attrs: extra || {},
|
|
47
|
-
});
|
|
48
|
-
}),
|
|
49
|
-
} as unknown as Logger;
|
|
50
|
-
|
|
51
|
-
mockOTelLogger = {
|
|
52
|
-
emit: vi.fn(),
|
|
53
|
-
} as unknown as ReturnType<typeof logs.getLogger>;
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
function createMockSpan(overrides: Partial<ReadableSpan> = {}): ReadableSpan {
|
|
57
|
-
const defaultSpan: Partial<ReadableSpan> = {
|
|
58
|
-
name: 'test.operation',
|
|
59
|
-
spanContext: () => ({
|
|
60
|
-
traceId: '4bf92f3577b34da6a3ce929d0e0e4736',
|
|
61
|
-
spanId: '00f067aa0ba902b7',
|
|
62
|
-
traceFlags: 1,
|
|
63
|
-
}),
|
|
64
|
-
parentSpanContext: undefined,
|
|
65
|
-
attributes: {
|
|
66
|
-
'user.id': 'user-123',
|
|
67
|
-
'cart.total_cents': 15_999,
|
|
68
|
-
'http.method': 'POST',
|
|
69
|
-
},
|
|
70
|
-
status: { code: SpanStatusCode.OK },
|
|
71
|
-
duration: [0, 1_247_000_000], // 1.247 seconds in nanoseconds
|
|
72
|
-
startTime: [1_703_044_800, 0], // Unix timestamp in nanoseconds
|
|
73
|
-
endTime: [1_703_044_800, 1_247_000_000],
|
|
74
|
-
kind: SpanKind.SERVER,
|
|
75
|
-
resource: resourceFromAttributes({
|
|
76
|
-
'service.name': 'test-service',
|
|
77
|
-
'service.version': '1.0.0',
|
|
78
|
-
}),
|
|
79
|
-
events: [],
|
|
80
|
-
links: [],
|
|
81
|
-
...overrides,
|
|
82
|
-
};
|
|
83
|
-
return defaultSpan as ReadableSpan;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
describe('basic functionality', () => {
|
|
87
|
-
it('should emit canonical log line with all span attributes', () => {
|
|
88
|
-
const processor = new CanonicalLogLineProcessor({ logger: mockLogger });
|
|
89
|
-
const span = createMockSpan();
|
|
90
|
-
|
|
91
|
-
processor.onEnd(span);
|
|
92
|
-
|
|
93
|
-
expect(mockLogger.info).toHaveBeenCalledTimes(1);
|
|
94
|
-
const call = logEntries[0];
|
|
95
|
-
expect(call.level).toBe('info');
|
|
96
|
-
expect(call.message).toContain('test.operation');
|
|
97
|
-
expect(call.attrs).toMatchObject({
|
|
98
|
-
operation: 'test.operation',
|
|
99
|
-
traceId: '4bf92f3577b34da6a3ce929d0e0e4736',
|
|
100
|
-
spanId: '00f067aa0ba902b7',
|
|
101
|
-
correlationId: '4bf92f3577b34da6',
|
|
102
|
-
'user.id': 'user-123',
|
|
103
|
-
'cart.total_cents': 15_999,
|
|
104
|
-
'http.method': 'POST',
|
|
105
|
-
duration_ms: expect.any(Number),
|
|
106
|
-
status_code: SpanStatusCode.OK,
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it('should include resource attributes by default', () => {
|
|
111
|
-
const processor = new CanonicalLogLineProcessor({ logger: mockLogger });
|
|
112
|
-
const span = createMockSpan();
|
|
113
|
-
|
|
114
|
-
processor.onEnd(span);
|
|
115
|
-
|
|
116
|
-
const call = logEntries[0];
|
|
117
|
-
expect(call.attrs).toMatchObject({
|
|
118
|
-
'service.name': 'test-service',
|
|
119
|
-
'service.version': '1.0.0',
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it('should exclude resource attributes when disabled', () => {
|
|
124
|
-
const processor = new CanonicalLogLineProcessor({
|
|
125
|
-
logger: mockLogger,
|
|
126
|
-
includeResourceAttributes: false,
|
|
127
|
-
});
|
|
128
|
-
const span = createMockSpan();
|
|
129
|
-
|
|
130
|
-
processor.onEnd(span);
|
|
131
|
-
|
|
132
|
-
const call = logEntries[0];
|
|
133
|
-
expect(call.attrs).not.toHaveProperty('service.name');
|
|
134
|
-
expect(call.attrs).not.toHaveProperty('service.version');
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
it('should calculate duration in milliseconds correctly', () => {
|
|
138
|
-
const processor = new CanonicalLogLineProcessor({ logger: mockLogger });
|
|
139
|
-
const span = createMockSpan({
|
|
140
|
-
duration: [0, 2_500_000_000], // 2.5 seconds
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
processor.onEnd(span);
|
|
144
|
-
|
|
145
|
-
const call = logEntries[0];
|
|
146
|
-
expect(call.attrs.duration_ms).toBeCloseTo(2500, 1);
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it('should format timestamp as ISO string', () => {
|
|
150
|
-
const processor = new CanonicalLogLineProcessor({ logger: mockLogger });
|
|
151
|
-
const span = createMockSpan({
|
|
152
|
-
startTime: [1_703_044_800, 0], // Fixed timestamp
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
processor.onEnd(span);
|
|
156
|
-
|
|
157
|
-
const call = logEntries[0];
|
|
158
|
-
expect(call.attrs.timestamp).toMatch(
|
|
159
|
-
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/,
|
|
160
|
-
);
|
|
161
|
-
});
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
describe('rootSpansOnly option', () => {
|
|
165
|
-
it('should emit log for root span when rootSpansOnly is true', () => {
|
|
166
|
-
const processor = new CanonicalLogLineProcessor({
|
|
167
|
-
logger: mockLogger,
|
|
168
|
-
rootSpansOnly: true,
|
|
169
|
-
});
|
|
170
|
-
const span = createMockSpan({ parentSpanContext: undefined });
|
|
171
|
-
|
|
172
|
-
processor.onEnd(span);
|
|
173
|
-
|
|
174
|
-
expect(mockLogger.info).toHaveBeenCalledTimes(1);
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
it('should skip child spans when rootSpansOnly is true', () => {
|
|
178
|
-
const processor = new CanonicalLogLineProcessor({
|
|
179
|
-
logger: mockLogger,
|
|
180
|
-
rootSpansOnly: true,
|
|
181
|
-
});
|
|
182
|
-
const span = createMockSpan({
|
|
183
|
-
parentSpanContext: {
|
|
184
|
-
traceId: '4bf92f3577b34da6a3ce929d0e0e4736',
|
|
185
|
-
spanId: 'local-parent-span-id',
|
|
186
|
-
traceFlags: 1,
|
|
187
|
-
isRemote: false,
|
|
188
|
-
},
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
processor.onEnd(span);
|
|
192
|
-
|
|
193
|
-
expect(mockLogger.info).not.toHaveBeenCalled();
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
it('should emit for spans with remote parent (distributed tracing)', () => {
|
|
197
|
-
const processor = new CanonicalLogLineProcessor({
|
|
198
|
-
logger: mockLogger,
|
|
199
|
-
rootSpansOnly: true,
|
|
200
|
-
});
|
|
201
|
-
const span = createMockSpan({
|
|
202
|
-
parentSpanContext: {
|
|
203
|
-
traceId: '4bf92f3577b34da6a3ce929d0e0e4736',
|
|
204
|
-
spanId: 'remote-parent-span-id',
|
|
205
|
-
traceFlags: 1,
|
|
206
|
-
isRemote: true,
|
|
207
|
-
},
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
processor.onEnd(span);
|
|
211
|
-
|
|
212
|
-
expect(mockLogger.info).toHaveBeenCalledTimes(1);
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
it('should emit log for all spans when rootSpansOnly is false', () => {
|
|
216
|
-
const processor = new CanonicalLogLineProcessor({
|
|
217
|
-
logger: mockLogger,
|
|
218
|
-
rootSpansOnly: false,
|
|
219
|
-
});
|
|
220
|
-
const span = createMockSpan({
|
|
221
|
-
parentSpanContext: {
|
|
222
|
-
traceId: '4bf92f3577b34da6a3ce929d0e0e4736',
|
|
223
|
-
spanId: 'parent-span-id',
|
|
224
|
-
traceFlags: 1,
|
|
225
|
-
isRemote: false,
|
|
226
|
-
},
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
processor.onEnd(span);
|
|
230
|
-
|
|
231
|
-
expect(mockLogger.info).toHaveBeenCalledTimes(1);
|
|
232
|
-
});
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
describe('log level determination', () => {
|
|
236
|
-
it('should use error level for error spans', () => {
|
|
237
|
-
const processor = new CanonicalLogLineProcessor({ logger: mockLogger });
|
|
238
|
-
const span = createMockSpan({
|
|
239
|
-
status: {
|
|
240
|
-
code: SpanStatusCode.ERROR,
|
|
241
|
-
message: 'Something went wrong',
|
|
242
|
-
},
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
processor.onEnd(span);
|
|
246
|
-
|
|
247
|
-
expect(mockLogger.error).toHaveBeenCalledTimes(1);
|
|
248
|
-
expect(logEntries[0].level).toBe('error');
|
|
249
|
-
expect(logEntries[0].attrs.status_code).toBe(SpanStatusCode.ERROR);
|
|
250
|
-
expect(logEntries[0].attrs.status_message).toBe('Something went wrong');
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
it('should use info level for successful spans', () => {
|
|
254
|
-
const processor = new CanonicalLogLineProcessor({ logger: mockLogger });
|
|
255
|
-
const span = createMockSpan({
|
|
256
|
-
status: { code: SpanStatusCode.OK },
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
processor.onEnd(span);
|
|
260
|
-
|
|
261
|
-
expect(mockLogger.info).toHaveBeenCalledTimes(1);
|
|
262
|
-
expect(logEntries[0].level).toBe('info');
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
it('should use explicit autotel.log.level attribute when provided', () => {
|
|
266
|
-
const processor = new CanonicalLogLineProcessor({ logger: mockLogger });
|
|
267
|
-
const span = createMockSpan({
|
|
268
|
-
attributes: {
|
|
269
|
-
'autotel.log.level': 'warn',
|
|
270
|
-
},
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
processor.onEnd(span);
|
|
274
|
-
|
|
275
|
-
expect(mockLogger.warn).toHaveBeenCalledTimes(1);
|
|
276
|
-
expect(logEntries[0].level).toBe('warn');
|
|
277
|
-
});
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
describe('minLevel option', () => {
|
|
281
|
-
it('should respect minLevel and skip debug logs', () => {
|
|
282
|
-
const processor = new CanonicalLogLineProcessor({
|
|
283
|
-
logger: mockLogger,
|
|
284
|
-
minLevel: 'info',
|
|
285
|
-
});
|
|
286
|
-
const span = createMockSpan();
|
|
287
|
-
|
|
288
|
-
processor.onEnd(span);
|
|
289
|
-
|
|
290
|
-
expect(mockLogger.info).toHaveBeenCalledTimes(1);
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
it('should skip logs below minLevel', () => {
|
|
294
|
-
const processor = new CanonicalLogLineProcessor({
|
|
295
|
-
logger: mockLogger,
|
|
296
|
-
minLevel: 'warn',
|
|
297
|
-
});
|
|
298
|
-
const span = createMockSpan({
|
|
299
|
-
status: { code: SpanStatusCode.OK },
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
processor.onEnd(span);
|
|
303
|
-
|
|
304
|
-
expect(mockLogger.info).not.toHaveBeenCalled();
|
|
305
|
-
});
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
describe('custom message format', () => {
|
|
309
|
-
it('should use custom message format when provided', () => {
|
|
310
|
-
const processor = new CanonicalLogLineProcessor({
|
|
311
|
-
logger: mockLogger,
|
|
312
|
-
messageFormat: (span) => {
|
|
313
|
-
const status = span.status.code === 2 ? 'ERROR' : 'SUCCESS';
|
|
314
|
-
return `[${status}] ${span.name}`;
|
|
315
|
-
},
|
|
316
|
-
});
|
|
317
|
-
const span = createMockSpan();
|
|
318
|
-
|
|
319
|
-
processor.onEnd(span);
|
|
320
|
-
|
|
321
|
-
expect(logEntries[0].message).toBe('[SUCCESS] test.operation');
|
|
322
|
-
});
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
describe('emit control hooks', () => {
|
|
326
|
-
it('should skip emit when shouldEmit returns false', () => {
|
|
327
|
-
const shouldEmit = vi.fn(() => false);
|
|
328
|
-
const processor = new CanonicalLogLineProcessor({
|
|
329
|
-
logger: mockLogger,
|
|
330
|
-
shouldEmit,
|
|
331
|
-
});
|
|
332
|
-
const span = createMockSpan();
|
|
333
|
-
|
|
334
|
-
processor.onEnd(span);
|
|
335
|
-
|
|
336
|
-
expect(shouldEmit).toHaveBeenCalledTimes(1);
|
|
337
|
-
expect(mockLogger.info).not.toHaveBeenCalled();
|
|
338
|
-
});
|
|
339
|
-
|
|
340
|
-
it('should call drain after emit with canonical event context', async () => {
|
|
341
|
-
const drain = vi.fn(async () => {});
|
|
342
|
-
const processor = new CanonicalLogLineProcessor({
|
|
343
|
-
logger: mockLogger,
|
|
344
|
-
drain,
|
|
345
|
-
});
|
|
346
|
-
const span = createMockSpan();
|
|
347
|
-
|
|
348
|
-
processor.onEnd(span);
|
|
349
|
-
await new Promise((resolve) => setImmediate(resolve));
|
|
350
|
-
|
|
351
|
-
expect(mockLogger.info).toHaveBeenCalledTimes(1);
|
|
352
|
-
expect(drain).toHaveBeenCalledTimes(1);
|
|
353
|
-
expect(drain.mock.calls[0][0]).toMatchObject({
|
|
354
|
-
level: 'info',
|
|
355
|
-
message: expect.stringContaining('test.operation'),
|
|
356
|
-
event: expect.objectContaining({
|
|
357
|
-
operation: 'test.operation',
|
|
358
|
-
traceId: '4bf92f3577b34da6a3ce929d0e0e4736',
|
|
359
|
-
}),
|
|
360
|
-
});
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
it('should not keep below-threshold HTTP status when keep.status is configured', () => {
|
|
364
|
-
const processor = new CanonicalLogLineProcessor({
|
|
365
|
-
logger: mockLogger,
|
|
366
|
-
keep: [{ status: 500 }],
|
|
367
|
-
});
|
|
368
|
-
const span = createMockSpan({
|
|
369
|
-
status: { code: SpanStatusCode.ERROR },
|
|
370
|
-
attributes: {
|
|
371
|
-
'http.response.status_code': 404,
|
|
372
|
-
},
|
|
373
|
-
});
|
|
374
|
-
|
|
375
|
-
processor.onEnd(span);
|
|
376
|
-
|
|
377
|
-
expect(mockLogger.error).not.toHaveBeenCalled();
|
|
378
|
-
expect(mockLogger.info).not.toHaveBeenCalled();
|
|
379
|
-
expect(mockLogger.warn).not.toHaveBeenCalled();
|
|
380
|
-
expect(mockLogger.debug).not.toHaveBeenCalled();
|
|
381
|
-
});
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
describe('OTel Logs API fallback', () => {
|
|
385
|
-
it('should use OTel Logs API when no logger provided', () => {
|
|
386
|
-
// Mock the logs API
|
|
387
|
-
const mockGetLogger = vi.fn(() => mockOTelLogger);
|
|
388
|
-
vi.spyOn(logs, 'getLogger').mockImplementation(mockGetLogger);
|
|
389
|
-
|
|
390
|
-
const processor = new CanonicalLogLineProcessor();
|
|
391
|
-
const span = createMockSpan();
|
|
392
|
-
|
|
393
|
-
processor.onEnd(span);
|
|
394
|
-
|
|
395
|
-
expect(mockGetLogger).toHaveBeenCalledWith('autotel.canonical-log-line');
|
|
396
|
-
expect(mockOTelLogger.emit).toHaveBeenCalledTimes(1);
|
|
397
|
-
const emitCall = (mockOTelLogger.emit as ReturnType<typeof vi.fn>).mock
|
|
398
|
-
.calls[0][0];
|
|
399
|
-
expect(emitCall.body).toContain('test.operation');
|
|
400
|
-
expect(emitCall.attributes).toMatchObject({
|
|
401
|
-
operation: 'test.operation',
|
|
402
|
-
traceId: '4bf92f3577b34da6a3ce929d0e0e4736',
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
vi.restoreAllMocks();
|
|
406
|
-
});
|
|
407
|
-
});
|
|
408
|
-
|
|
409
|
-
describe('edge cases', () => {
|
|
410
|
-
it('should handle spans with no attributes', () => {
|
|
411
|
-
const processor = new CanonicalLogLineProcessor({ logger: mockLogger });
|
|
412
|
-
const span = createMockSpan({ attributes: {} });
|
|
413
|
-
|
|
414
|
-
processor.onEnd(span);
|
|
415
|
-
|
|
416
|
-
expect(mockLogger.info).toHaveBeenCalledTimes(1);
|
|
417
|
-
const call = logEntries[0];
|
|
418
|
-
expect(call.attrs).toHaveProperty('operation');
|
|
419
|
-
expect(call.attrs).toHaveProperty('traceId');
|
|
420
|
-
expect(call.attrs).toHaveProperty('spanId');
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
it('should handle spans with many attributes', () => {
|
|
424
|
-
const processor = new CanonicalLogLineProcessor({ logger: mockLogger });
|
|
425
|
-
const manyAttrs: Record<string, unknown> = {};
|
|
426
|
-
for (let i = 0; i < 100; i++) {
|
|
427
|
-
manyAttrs[`attr.${i}`] = `value-${i}`;
|
|
428
|
-
}
|
|
429
|
-
const span = createMockSpan({ attributes: manyAttrs });
|
|
430
|
-
|
|
431
|
-
processor.onEnd(span);
|
|
432
|
-
|
|
433
|
-
expect(mockLogger.info).toHaveBeenCalledTimes(1);
|
|
434
|
-
const call = logEntries[0];
|
|
435
|
-
expect(Object.keys(call.attrs).length).toBeGreaterThan(100);
|
|
436
|
-
});
|
|
437
|
-
|
|
438
|
-
it('should handle missing status message gracefully', () => {
|
|
439
|
-
const processor = new CanonicalLogLineProcessor({ logger: mockLogger });
|
|
440
|
-
const span = createMockSpan({
|
|
441
|
-
status: { code: SpanStatusCode.OK },
|
|
442
|
-
});
|
|
443
|
-
|
|
444
|
-
processor.onEnd(span);
|
|
445
|
-
|
|
446
|
-
const call = logEntries[0];
|
|
447
|
-
expect(call.attrs.status_message).toBeUndefined();
|
|
448
|
-
});
|
|
449
|
-
});
|
|
450
|
-
|
|
451
|
-
describe('attribute redaction', () => {
|
|
452
|
-
it('should apply attribute redactor to span attributes', () => {
|
|
453
|
-
const redactor = vi.fn((key: string, value: unknown) => {
|
|
454
|
-
if (key === 'user.password') return '[REDACTED]';
|
|
455
|
-
if (key === 'user.email' && typeof value === 'string') {
|
|
456
|
-
return value.replace(/@.*/, '@[REDACTED]');
|
|
457
|
-
}
|
|
458
|
-
return value;
|
|
459
|
-
});
|
|
460
|
-
|
|
461
|
-
const processor = new CanonicalLogLineProcessor({
|
|
462
|
-
logger: mockLogger,
|
|
463
|
-
attributeRedactor: redactor,
|
|
464
|
-
});
|
|
465
|
-
const span = createMockSpan({
|
|
466
|
-
attributes: {
|
|
467
|
-
'user.id': 'user-123',
|
|
468
|
-
'user.email': 'alice@example.com',
|
|
469
|
-
'user.password': 'secret123',
|
|
470
|
-
},
|
|
471
|
-
});
|
|
472
|
-
|
|
473
|
-
processor.onEnd(span);
|
|
474
|
-
|
|
475
|
-
const call = logEntries[0];
|
|
476
|
-
expect(call.attrs['user.id']).toBe('user-123');
|
|
477
|
-
expect(call.attrs['user.email']).toBe('alice@[REDACTED]');
|
|
478
|
-
expect(call.attrs['user.password']).toBe('[REDACTED]');
|
|
479
|
-
});
|
|
480
|
-
|
|
481
|
-
it('should not modify attributes when no redactor configured', () => {
|
|
482
|
-
const processor = new CanonicalLogLineProcessor({ logger: mockLogger });
|
|
483
|
-
const span = createMockSpan({
|
|
484
|
-
attributes: {
|
|
485
|
-
'user.password': 'secret123',
|
|
486
|
-
},
|
|
487
|
-
});
|
|
488
|
-
|
|
489
|
-
processor.onEnd(span);
|
|
490
|
-
|
|
491
|
-
const call = logEntries[0];
|
|
492
|
-
expect(call.attrs['user.password']).toBe('secret123');
|
|
493
|
-
});
|
|
494
|
-
});
|
|
495
|
-
|
|
496
|
-
describe('attribute collision prevention', () => {
|
|
497
|
-
it('should not allow span attributes to overwrite core metadata', () => {
|
|
498
|
-
const processor = new CanonicalLogLineProcessor({ logger: mockLogger });
|
|
499
|
-
const span = createMockSpan({
|
|
500
|
-
attributes: {
|
|
501
|
-
traceId: 'malicious-trace-id',
|
|
502
|
-
spanId: 'malicious-span-id',
|
|
503
|
-
timestamp: 'malicious-timestamp',
|
|
504
|
-
operation: 'malicious-operation',
|
|
505
|
-
duration_ms: 999_999,
|
|
506
|
-
status_code: 42,
|
|
507
|
-
correlationId: 'malicious-correlation',
|
|
508
|
-
},
|
|
509
|
-
});
|
|
510
|
-
|
|
511
|
-
processor.onEnd(span);
|
|
512
|
-
|
|
513
|
-
const call = logEntries[0];
|
|
514
|
-
expect(call.attrs.traceId).toBe('4bf92f3577b34da6a3ce929d0e0e4736');
|
|
515
|
-
expect(call.attrs.spanId).toBe('00f067aa0ba902b7');
|
|
516
|
-
expect(call.attrs.operation).toBe('test.operation');
|
|
517
|
-
expect(call.attrs.correlationId).toBe('4bf92f3577b34da6');
|
|
518
|
-
expect(call.attrs.status_code).toBe(SpanStatusCode.OK);
|
|
519
|
-
expect(call.attrs.duration_ms).toBeCloseTo(1247, 0);
|
|
520
|
-
expect(call.attrs.timestamp).toMatch(/^\d{4}-\d{2}-\d{2}T/);
|
|
521
|
-
});
|
|
522
|
-
});
|
|
523
|
-
});
|