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
package/src/config.ts
DELETED
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Global configuration for OpenTelemetry decorators
|
|
3
|
-
*
|
|
4
|
-
* Allows users to inject custom loggers, tracers, and meters
|
|
5
|
-
* while maintaining sensible defaults.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { trace, metrics, type Tracer, type Meter } from '@opentelemetry/api';
|
|
9
|
-
import { getAutotelTracer } from './tracer-provider';
|
|
10
|
-
|
|
11
|
-
export type { ILogger } from './logger';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Environment-based feature flags for performance optimization
|
|
15
|
-
*
|
|
16
|
-
* Disables expensive features in development while maintaining
|
|
17
|
-
* full observability in production.
|
|
18
|
-
*/
|
|
19
|
-
const IS_PRODUCTION = process.env.NODE_ENV === 'production';
|
|
20
|
-
const IS_DEV = process.env.NODE_ENV === 'development';
|
|
21
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
22
|
-
const IS_TEST = process.env.NODE_ENV === 'test';
|
|
23
|
-
|
|
24
|
-
export const FEATURE_FLAGS = {
|
|
25
|
-
/** Enable full auto-instrumentation (expensive, production only) */
|
|
26
|
-
ENABLE_AUTO_INSTRUMENTATION:
|
|
27
|
-
IS_PRODUCTION && process.env.autotel_AUTO_INSTRUMENT !== 'false',
|
|
28
|
-
|
|
29
|
-
/** Enable verbose logging (development only) */
|
|
30
|
-
ENABLE_VERBOSE_LOGGING: IS_DEV || process.env.autotel_VERBOSE === 'true',
|
|
31
|
-
|
|
32
|
-
/** Enable metrics collection (production only) */
|
|
33
|
-
ENABLE_METRICS_BY_DEFAULT:
|
|
34
|
-
IS_PRODUCTION && process.env.autotel_METRICS !== 'false',
|
|
35
|
-
|
|
36
|
-
/** Enable async resource detection (production only) */
|
|
37
|
-
ENABLE_RESOURCE_DETECTION:
|
|
38
|
-
IS_PRODUCTION && process.env.autotel_RESOURCE_DETECTION === 'true',
|
|
39
|
-
|
|
40
|
-
/** Enable tracing in all environments (can be disabled via autotel_TRACING=false) */
|
|
41
|
-
ENABLE_TRACING: process.env.autotel_TRACING !== 'false',
|
|
42
|
-
|
|
43
|
-
/** Enable log redaction for sensitive fields (can be disabled via autotel_REDACTION=false) */
|
|
44
|
-
ENABLE_REDACTION: process.env.autotel_REDACTION !== 'false',
|
|
45
|
-
} as const;
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Runtime configuration for OpenTelemetry instrumentation
|
|
49
|
-
*
|
|
50
|
-
* This configures the tracer and meter used by autotel's functional API.
|
|
51
|
-
* Use `configure()` to set custom tracer/meter instances.
|
|
52
|
-
*/
|
|
53
|
-
export interface RuntimeConfig {
|
|
54
|
-
/**
|
|
55
|
-
* Tracer name for OpenTelemetry
|
|
56
|
-
* @default 'app'
|
|
57
|
-
*/
|
|
58
|
-
tracerName?: string;
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Meter name for OpenTelemetry metrics
|
|
62
|
-
* @default 'app'
|
|
63
|
-
*/
|
|
64
|
-
meterName?: string;
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Custom tracer instance (for advanced use cases like Datadog direct)
|
|
68
|
-
* @default trace.getTracer(tracerName)
|
|
69
|
-
*/
|
|
70
|
-
tracer?: Tracer;
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Custom meter instance
|
|
74
|
-
* @default metrics.getMeter(meterName)
|
|
75
|
-
*/
|
|
76
|
-
meter?: Meter;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Internal configuration state
|
|
81
|
-
*/
|
|
82
|
-
class Config {
|
|
83
|
-
private config: Required<RuntimeConfig> = {
|
|
84
|
-
tracerName: 'app',
|
|
85
|
-
meterName: 'app',
|
|
86
|
-
tracer: getAutotelTracer('app'),
|
|
87
|
-
meter: metrics.getMeter('app'),
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Get feature flags
|
|
92
|
-
*/
|
|
93
|
-
get featureFlags() {
|
|
94
|
-
return FEATURE_FLAGS;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Update global configuration
|
|
99
|
-
*/
|
|
100
|
-
configure(options: RuntimeConfig): void {
|
|
101
|
-
if (options.tracerName) {
|
|
102
|
-
this.config.tracerName = options.tracerName;
|
|
103
|
-
this.config.tracer = getAutotelTracer(options.tracerName);
|
|
104
|
-
}
|
|
105
|
-
if (options.meterName) {
|
|
106
|
-
this.config.meterName = options.meterName;
|
|
107
|
-
this.config.meter = metrics.getMeter(options.meterName);
|
|
108
|
-
}
|
|
109
|
-
if (options.tracer) {
|
|
110
|
-
this.config.tracer = options.tracer;
|
|
111
|
-
}
|
|
112
|
-
if (options.meter) {
|
|
113
|
-
this.config.meter = options.meter;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Get current configuration
|
|
119
|
-
*/
|
|
120
|
-
get(): Required<RuntimeConfig> {
|
|
121
|
-
return this.config;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Reset to defaults (mainly for testing)
|
|
126
|
-
*/
|
|
127
|
-
reset(): void {
|
|
128
|
-
this.config = {
|
|
129
|
-
tracerName: 'app',
|
|
130
|
-
meterName: 'app',
|
|
131
|
-
tracer: trace.getTracer('app'),
|
|
132
|
-
meter: metrics.getMeter('app'),
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const globalConfig = new Config();
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Configure global instrumentation behavior
|
|
141
|
-
*
|
|
142
|
-
* @example
|
|
143
|
-
* ```typescript
|
|
144
|
-
* import { configure } from 'autotel/config'
|
|
145
|
-
*
|
|
146
|
-
* configure({
|
|
147
|
-
* tracerName: 'my-app'
|
|
148
|
-
* })
|
|
149
|
-
* ```
|
|
150
|
-
*/
|
|
151
|
-
export function configure(options: RuntimeConfig): void {
|
|
152
|
-
globalConfig.configure(options);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Get current configuration (internal use)
|
|
157
|
-
*/
|
|
158
|
-
export function getConfig(): Required<RuntimeConfig> & {
|
|
159
|
-
featureFlags: typeof FEATURE_FLAGS;
|
|
160
|
-
} {
|
|
161
|
-
return {
|
|
162
|
-
...globalConfig.get(),
|
|
163
|
-
featureFlags: FEATURE_FLAGS,
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Reset configuration to defaults (internal use - mainly for testing)
|
|
169
|
-
*/
|
|
170
|
-
export function resetConfig(): void {
|
|
171
|
-
globalConfig.reset();
|
|
172
|
-
}
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
-
import {
|
|
3
|
-
emitCorrelatedEvent,
|
|
4
|
-
type CorrelatedEventTarget,
|
|
5
|
-
} from './correlated-events';
|
|
6
|
-
|
|
7
|
-
function makeTarget(opts: { withAddEvent: boolean }): {
|
|
8
|
-
target: CorrelatedEventTarget;
|
|
9
|
-
setAttribute: ReturnType<typeof vi.fn>;
|
|
10
|
-
setAttributes: ReturnType<typeof vi.fn>;
|
|
11
|
-
addEvent: ReturnType<typeof vi.fn> | undefined;
|
|
12
|
-
} {
|
|
13
|
-
const setAttribute = vi.fn();
|
|
14
|
-
const setAttributes = vi.fn();
|
|
15
|
-
const addEvent = opts.withAddEvent ? vi.fn() : undefined;
|
|
16
|
-
const target: CorrelatedEventTarget = addEvent
|
|
17
|
-
? { setAttribute, setAttributes, addEvent }
|
|
18
|
-
: { setAttribute, setAttributes };
|
|
19
|
-
return { target, setAttribute, setAttributes, addEvent };
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
describe('emitCorrelatedEvent', () => {
|
|
23
|
-
describe('addEvent path', () => {
|
|
24
|
-
it('forwards to addEvent when present and skips the attribute fallback', () => {
|
|
25
|
-
const { target, setAttribute, setAttributes, addEvent } = makeTarget({
|
|
26
|
-
withAddEvent: true,
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
emitCorrelatedEvent(target, 'gen_ai.prompt.sent', {
|
|
30
|
-
'gen_ai.system': 'openai',
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
expect(addEvent).toHaveBeenCalledTimes(1);
|
|
34
|
-
expect(addEvent).toHaveBeenCalledWith('gen_ai.prompt.sent', {
|
|
35
|
-
'gen_ai.system': 'openai',
|
|
36
|
-
});
|
|
37
|
-
expect(setAttribute).not.toHaveBeenCalled();
|
|
38
|
-
expect(setAttributes).not.toHaveBeenCalled();
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it('sanitizes the event name before forwarding', () => {
|
|
42
|
-
const { target, addEvent } = makeTarget({ withAddEvent: true });
|
|
43
|
-
|
|
44
|
-
emitCorrelatedEvent(target, 'gen ai/prompt sent!', {});
|
|
45
|
-
|
|
46
|
-
expect(addEvent).toHaveBeenCalledWith('gen_ai_prompt_sent_', {});
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('preserves `this` when calling addEvent (works for prototype methods)', () => {
|
|
50
|
-
const captured: { self: unknown; args: unknown[] } = {
|
|
51
|
-
self: null,
|
|
52
|
-
args: [],
|
|
53
|
-
};
|
|
54
|
-
const target = {
|
|
55
|
-
setAttribute: vi.fn(),
|
|
56
|
-
setAttributes: vi.fn(),
|
|
57
|
-
addEvent(this: unknown, ...args: unknown[]) {
|
|
58
|
-
captured.self = this;
|
|
59
|
-
captured.args = args;
|
|
60
|
-
},
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
emitCorrelatedEvent(target, 'evt', { k: 'v' });
|
|
64
|
-
|
|
65
|
-
expect(captured.self).toBe(target);
|
|
66
|
-
expect(captured.args).toEqual(['evt', { k: 'v' }]);
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
describe('attribute fallback', () => {
|
|
71
|
-
it('writes flat, sequence-prefixed attributes when addEvent is missing', () => {
|
|
72
|
-
const { target, setAttributes } = makeTarget({ withAddEvent: false });
|
|
73
|
-
|
|
74
|
-
emitCorrelatedEvent(target, 'workflow.started', {
|
|
75
|
-
'workflow.id': 'wf-1',
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
expect(setAttributes).toHaveBeenCalledTimes(1);
|
|
79
|
-
const written = setAttributes.mock.calls[0]![0] as Record<
|
|
80
|
-
string,
|
|
81
|
-
unknown
|
|
82
|
-
>;
|
|
83
|
-
|
|
84
|
-
expect(written['autotel.event.1.workflow.started.name']).toBe(
|
|
85
|
-
'workflow.started',
|
|
86
|
-
);
|
|
87
|
-
expect(typeof written['autotel.event.1.workflow.started.ts']).toBe(
|
|
88
|
-
'string',
|
|
89
|
-
);
|
|
90
|
-
expect(written['autotel.event.1.workflow.started.workflow.id']).toBe(
|
|
91
|
-
'wf-1',
|
|
92
|
-
);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it('does not overwrite earlier events when the same name fires twice', () => {
|
|
96
|
-
const { target, setAttributes } = makeTarget({ withAddEvent: false });
|
|
97
|
-
|
|
98
|
-
emitCorrelatedEvent(target, 'step_retry', { 'workflow.step.attempt': 1 });
|
|
99
|
-
emitCorrelatedEvent(target, 'step_retry', { 'workflow.step.attempt': 2 });
|
|
100
|
-
|
|
101
|
-
expect(setAttributes).toHaveBeenCalledTimes(2);
|
|
102
|
-
const first = setAttributes.mock.calls[0]![0] as Record<string, unknown>;
|
|
103
|
-
const second = setAttributes.mock.calls[1]![0] as Record<string, unknown>;
|
|
104
|
-
|
|
105
|
-
expect(first['autotel.event.1.step_retry.workflow.step.attempt']).toBe(1);
|
|
106
|
-
expect(second['autotel.event.2.step_retry.workflow.step.attempt']).toBe(
|
|
107
|
-
2,
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
// Different keys: second call cannot overwrite the first when both
|
|
111
|
-
// attribute sets are merged on the same span.
|
|
112
|
-
expect(
|
|
113
|
-
Object.keys(first).every((k) => !Object.keys(second).includes(k)),
|
|
114
|
-
).toBe(true);
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
it('sanitizes attribute keys in the fallback path', () => {
|
|
118
|
-
const { target, setAttributes } = makeTarget({ withAddEvent: false });
|
|
119
|
-
|
|
120
|
-
emitCorrelatedEvent(target, 'evt', { 'has spaces/and-bad!': 1 });
|
|
121
|
-
|
|
122
|
-
const written = setAttributes.mock.calls[0]![0] as Record<
|
|
123
|
-
string,
|
|
124
|
-
unknown
|
|
125
|
-
>;
|
|
126
|
-
const key = Object.keys(written).find((k) =>
|
|
127
|
-
k.endsWith('has_spaces_and-bad_'),
|
|
128
|
-
);
|
|
129
|
-
expect(key).toBeDefined();
|
|
130
|
-
expect(written[key!]).toBe(1);
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it('keeps separate sequences for separate targets', () => {
|
|
134
|
-
const a = makeTarget({ withAddEvent: false });
|
|
135
|
-
const b = makeTarget({ withAddEvent: false });
|
|
136
|
-
|
|
137
|
-
emitCorrelatedEvent(a.target, 'evt', {});
|
|
138
|
-
emitCorrelatedEvent(b.target, 'evt', {});
|
|
139
|
-
|
|
140
|
-
const aKeys = Object.keys(
|
|
141
|
-
a.setAttributes.mock.calls[0]![0] as Record<string, unknown>,
|
|
142
|
-
);
|
|
143
|
-
const bKeys = Object.keys(
|
|
144
|
-
b.setAttributes.mock.calls[0]![0] as Record<string, unknown>,
|
|
145
|
-
);
|
|
146
|
-
|
|
147
|
-
expect(aKeys.some((k) => k.startsWith('autotel.event.1.'))).toBe(true);
|
|
148
|
-
expect(bKeys.some((k) => k.startsWith('autotel.event.1.'))).toBe(true);
|
|
149
|
-
});
|
|
150
|
-
});
|
|
151
|
-
});
|
package/src/correlated-events.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import type { AttributeValue } from './trace-context';
|
|
2
|
-
|
|
3
|
-
export interface CorrelatedEventTarget {
|
|
4
|
-
setAttribute(key: string, value: AttributeValue): unknown;
|
|
5
|
-
setAttributes(attrs: Record<string, AttributeValue>): unknown;
|
|
6
|
-
addEvent?(name: string, attrs?: Record<string, AttributeValue>): unknown;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
// OTel attribute keys are dot-namespaced flat strings; we keep `.`/`-`/`_` and
|
|
10
|
-
// drop everything else so user-supplied event names can't break attribute keys.
|
|
11
|
-
function sanitizeEventKey(input: string): string {
|
|
12
|
-
return input.replaceAll(/[^a-zA-Z0-9_.-]/g, '_');
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// Per-target sequence so the fallback path can encode multiple events with the
|
|
16
|
-
// same name without one overwriting the previous (attributes are
|
|
17
|
-
// last-write-wins; events are not). Today the addEvent path is always taken;
|
|
18
|
-
// this keeps the fallback correct if/when the runtime stops binding addEvent.
|
|
19
|
-
const sequenceByTarget = new WeakMap<object, number>();
|
|
20
|
-
|
|
21
|
-
function nextSequence(target: object): number {
|
|
22
|
-
const n = (sequenceByTarget.get(target) ?? 0) + 1;
|
|
23
|
-
sequenceByTarget.set(target, n);
|
|
24
|
-
return n;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function emitCorrelatedEvent(
|
|
28
|
-
ctx: CorrelatedEventTarget,
|
|
29
|
-
name: string,
|
|
30
|
-
attrs: Record<string, AttributeValue> = {},
|
|
31
|
-
): void {
|
|
32
|
-
const eventName = sanitizeEventKey(name);
|
|
33
|
-
if (typeof ctx.addEvent === 'function') {
|
|
34
|
-
ctx.addEvent.call(ctx, eventName, attrs);
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
const seq = nextSequence(ctx);
|
|
38
|
-
const prefix = `autotel.event.${seq}.${eventName}`;
|
|
39
|
-
const flattened: Record<string, AttributeValue> = {
|
|
40
|
-
[`${prefix}.name`]: eventName,
|
|
41
|
-
[`${prefix}.ts`]: new Date().toISOString(),
|
|
42
|
-
};
|
|
43
|
-
for (const [k, v] of Object.entries(attrs)) {
|
|
44
|
-
flattened[`${prefix}.${sanitizeEventKey(k)}`] = v;
|
|
45
|
-
}
|
|
46
|
-
ctx.setAttributes(flattened);
|
|
47
|
-
}
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for correlation ID generation and propagation
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
6
|
-
import {
|
|
7
|
-
generateCorrelationId,
|
|
8
|
-
getCorrelationId,
|
|
9
|
-
getOrCreateCorrelationId,
|
|
10
|
-
runWithCorrelationId,
|
|
11
|
-
getCorrelationStorage,
|
|
12
|
-
} from './correlation-id';
|
|
13
|
-
|
|
14
|
-
describe('Correlation ID', () => {
|
|
15
|
-
beforeEach(() => {
|
|
16
|
-
// Clear the async local storage before each test
|
|
17
|
-
// Run with undefined context to clear - safer than enterWith with type assertion
|
|
18
|
-
getCorrelationStorage().disable();
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
describe('generateCorrelationId', () => {
|
|
22
|
-
it('should generate 16 hex character ID', () => {
|
|
23
|
-
const id = generateCorrelationId();
|
|
24
|
-
expect(id).toHaveLength(16);
|
|
25
|
-
expect(/^[0-9a-f]{16}$/.test(id)).toBe(true);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('should generate unique IDs', () => {
|
|
29
|
-
const ids = new Set<string>();
|
|
30
|
-
for (let i = 0; i < 100; i++) {
|
|
31
|
-
ids.add(generateCorrelationId());
|
|
32
|
-
}
|
|
33
|
-
// All 100 IDs should be unique
|
|
34
|
-
expect(ids.size).toBe(100);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('should generate URL-safe IDs (hex only)', () => {
|
|
38
|
-
for (let i = 0; i < 50; i++) {
|
|
39
|
-
const id = generateCorrelationId();
|
|
40
|
-
// Should only contain hex characters
|
|
41
|
-
expect(/^[0-9a-f]+$/.test(id)).toBe(true);
|
|
42
|
-
// Should be URL-safe (no special characters)
|
|
43
|
-
expect(encodeURIComponent(id)).toBe(id);
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
describe('getCorrelationId', () => {
|
|
49
|
-
it('should return undefined when no correlation ID is set', () => {
|
|
50
|
-
const id = getCorrelationId();
|
|
51
|
-
expect(id).toBeUndefined();
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
describe('runWithCorrelationId', () => {
|
|
56
|
-
it('should set correlation ID within callback', () => {
|
|
57
|
-
const testId = 'test123456789012';
|
|
58
|
-
|
|
59
|
-
runWithCorrelationId(testId, () => {
|
|
60
|
-
expect(getCorrelationId()).toBe(testId);
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('should return callback result', () => {
|
|
65
|
-
const result = runWithCorrelationId('abc123def4567890', () => {
|
|
66
|
-
return 'test-result';
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
expect(result).toBe('test-result');
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('should isolate correlation ID to callback scope', () => {
|
|
73
|
-
expect(getCorrelationId()).toBeUndefined();
|
|
74
|
-
|
|
75
|
-
runWithCorrelationId('scoped-id-12345678', () => {
|
|
76
|
-
expect(getCorrelationId()).toBe('scoped-id-12345678');
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
// After callback, should be back to undefined
|
|
80
|
-
expect(getCorrelationId()).toBeUndefined();
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('should support nested runWithCorrelationId calls', () => {
|
|
84
|
-
runWithCorrelationId('outer-id-1234567', () => {
|
|
85
|
-
expect(getCorrelationId()).toBe('outer-id-1234567');
|
|
86
|
-
|
|
87
|
-
runWithCorrelationId('inner-id-7654321', () => {
|
|
88
|
-
expect(getCorrelationId()).toBe('inner-id-7654321');
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
// After inner callback, should be back to outer
|
|
92
|
-
expect(getCorrelationId()).toBe('outer-id-1234567');
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it('should work with async callbacks', async () => {
|
|
97
|
-
const result = await runWithCorrelationId('async-id-123456', async () => {
|
|
98
|
-
// Simulate async work
|
|
99
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
100
|
-
return getCorrelationId();
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
expect(result).toBe('async-id-123456');
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
describe('getOrCreateCorrelationId', () => {
|
|
108
|
-
it('should return existing correlation ID if set', () => {
|
|
109
|
-
const existingId = 'existing-id-1234';
|
|
110
|
-
|
|
111
|
-
runWithCorrelationId(existingId, () => {
|
|
112
|
-
const id = getOrCreateCorrelationId();
|
|
113
|
-
expect(id).toBe(existingId);
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
it('should generate new ID if not set', () => {
|
|
118
|
-
// No correlation ID set
|
|
119
|
-
const id = getOrCreateCorrelationId();
|
|
120
|
-
|
|
121
|
-
expect(id).toBeDefined();
|
|
122
|
-
expect(id).toHaveLength(16);
|
|
123
|
-
expect(/^[0-9a-f]{16}$/.test(id)).toBe(true);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it('should return same ID on repeated calls within same context', () => {
|
|
127
|
-
runWithCorrelationId('stable-id-123456', () => {
|
|
128
|
-
const id1 = getOrCreateCorrelationId();
|
|
129
|
-
const id2 = getOrCreateCorrelationId();
|
|
130
|
-
expect(id1).toBe(id2);
|
|
131
|
-
});
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
describe('format specification', () => {
|
|
136
|
-
it('should be exactly 16 characters (64 bits)', () => {
|
|
137
|
-
for (let i = 0; i < 20; i++) {
|
|
138
|
-
const id = generateCorrelationId();
|
|
139
|
-
expect(id.length).toBe(16);
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
it('should be crypto-random (not time-based)', () => {
|
|
144
|
-
// Generate two IDs in quick succession
|
|
145
|
-
const id1 = generateCorrelationId();
|
|
146
|
-
const id2 = generateCorrelationId();
|
|
147
|
-
|
|
148
|
-
// If time-based, first chars would likely be similar
|
|
149
|
-
// With crypto-random, they should be completely different
|
|
150
|
-
expect(id1).not.toBe(id2);
|
|
151
|
-
|
|
152
|
-
// Check that the IDs don't have a predictable pattern
|
|
153
|
-
// (This is a statistical test - very unlikely to fail with crypto-random)
|
|
154
|
-
const firstChars = [];
|
|
155
|
-
for (let i = 0; i < 10; i++) {
|
|
156
|
-
firstChars.push(generateCorrelationId()[0]);
|
|
157
|
-
}
|
|
158
|
-
// With crypto-random, we shouldn't see all the same first character
|
|
159
|
-
const uniqueFirstChars = new Set(firstChars);
|
|
160
|
-
expect(uniqueFirstChars.size).toBeGreaterThan(1);
|
|
161
|
-
});
|
|
162
|
-
});
|
|
163
|
-
});
|