autotel 2.1.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/LICENSE +21 -0
- package/README.md +1946 -0
- package/dist/chunk-2LNRY4QK.js +273 -0
- package/dist/chunk-2LNRY4QK.js.map +1 -0
- package/dist/chunk-3HENGDW2.js +587 -0
- package/dist/chunk-3HENGDW2.js.map +1 -0
- package/dist/chunk-4OAT42CA.cjs +73 -0
- package/dist/chunk-4OAT42CA.cjs.map +1 -0
- package/dist/chunk-5GWX5LFW.js +70 -0
- package/dist/chunk-5GWX5LFW.js.map +1 -0
- package/dist/chunk-5R2M36QB.js +195 -0
- package/dist/chunk-5R2M36QB.js.map +1 -0
- package/dist/chunk-5ZN622AO.js +73 -0
- package/dist/chunk-5ZN622AO.js.map +1 -0
- package/dist/chunk-77MSMAUQ.cjs +498 -0
- package/dist/chunk-77MSMAUQ.cjs.map +1 -0
- package/dist/chunk-ABPEQ6RK.cjs +596 -0
- package/dist/chunk-ABPEQ6RK.cjs.map +1 -0
- package/dist/chunk-BWYGJKRB.js +95 -0
- package/dist/chunk-BWYGJKRB.js.map +1 -0
- package/dist/chunk-BZHG5IZ4.js +73 -0
- package/dist/chunk-BZHG5IZ4.js.map +1 -0
- package/dist/chunk-G7VZBCD6.cjs +35 -0
- package/dist/chunk-G7VZBCD6.cjs.map +1 -0
- package/dist/chunk-GVLK7YUU.cjs +30 -0
- package/dist/chunk-GVLK7YUU.cjs.map +1 -0
- package/dist/chunk-HCCXC7XG.js +205 -0
- package/dist/chunk-HCCXC7XG.js.map +1 -0
- package/dist/chunk-HE6T6FIX.cjs +203 -0
- package/dist/chunk-HE6T6FIX.cjs.map +1 -0
- package/dist/chunk-KIXWPOCO.cjs +100 -0
- package/dist/chunk-KIXWPOCO.cjs.map +1 -0
- package/dist/chunk-KVGNW3FC.js +87 -0
- package/dist/chunk-KVGNW3FC.js.map +1 -0
- package/dist/chunk-LITNXTTT.js +3 -0
- package/dist/chunk-LITNXTTT.js.map +1 -0
- package/dist/chunk-M4ANN7RL.js +114 -0
- package/dist/chunk-M4ANN7RL.js.map +1 -0
- package/dist/chunk-NC52UBR2.cjs +32 -0
- package/dist/chunk-NC52UBR2.cjs.map +1 -0
- package/dist/chunk-NHCNRQD3.cjs +212 -0
- package/dist/chunk-NHCNRQD3.cjs.map +1 -0
- package/dist/chunk-NZ72VDNY.cjs +4 -0
- package/dist/chunk-NZ72VDNY.cjs.map +1 -0
- package/dist/chunk-P6JUDYNO.js +57 -0
- package/dist/chunk-P6JUDYNO.js.map +1 -0
- package/dist/chunk-RJYY7BWX.js +1349 -0
- package/dist/chunk-RJYY7BWX.js.map +1 -0
- package/dist/chunk-TRI4V5BF.cjs +126 -0
- package/dist/chunk-TRI4V5BF.cjs.map +1 -0
- package/dist/chunk-UL33I6IS.js +139 -0
- package/dist/chunk-UL33I6IS.js.map +1 -0
- package/dist/chunk-URRW6M2C.cjs +61 -0
- package/dist/chunk-URRW6M2C.cjs.map +1 -0
- package/dist/chunk-UY3UYPBZ.cjs +77 -0
- package/dist/chunk-UY3UYPBZ.cjs.map +1 -0
- package/dist/chunk-W3253FGB.cjs +277 -0
- package/dist/chunk-W3253FGB.cjs.map +1 -0
- package/dist/chunk-W7LHZVQF.js +26 -0
- package/dist/chunk-W7LHZVQF.js.map +1 -0
- package/dist/chunk-WBWNM6LB.cjs +1360 -0
- package/dist/chunk-WBWNM6LB.cjs.map +1 -0
- package/dist/chunk-WFJ7L2RV.js +494 -0
- package/dist/chunk-WFJ7L2RV.js.map +1 -0
- package/dist/chunk-X4RMFFMR.js +28 -0
- package/dist/chunk-X4RMFFMR.js.map +1 -0
- package/dist/chunk-Y4Y2S7BM.cjs +92 -0
- package/dist/chunk-Y4Y2S7BM.cjs.map +1 -0
- package/dist/chunk-YLPNXZFI.cjs +143 -0
- package/dist/chunk-YLPNXZFI.cjs.map +1 -0
- package/dist/chunk-YTXEZ4SD.cjs +77 -0
- package/dist/chunk-YTXEZ4SD.cjs.map +1 -0
- package/dist/chunk-Z6ZWNWWR.js +30 -0
- package/dist/chunk-Z6ZWNWWR.js.map +1 -0
- package/dist/config.cjs +26 -0
- package/dist/config.cjs.map +1 -0
- package/dist/config.d.cts +75 -0
- package/dist/config.d.ts +75 -0
- package/dist/config.js +5 -0
- package/dist/config.js.map +1 -0
- package/dist/db.cjs +233 -0
- package/dist/db.cjs.map +1 -0
- package/dist/db.d.cts +123 -0
- package/dist/db.d.ts +123 -0
- package/dist/db.js +228 -0
- package/dist/db.js.map +1 -0
- package/dist/decorators.cjs +67 -0
- package/dist/decorators.cjs.map +1 -0
- package/dist/decorators.d.cts +91 -0
- package/dist/decorators.d.ts +91 -0
- package/dist/decorators.js +65 -0
- package/dist/decorators.js.map +1 -0
- package/dist/event-subscriber.cjs +6 -0
- package/dist/event-subscriber.cjs.map +1 -0
- package/dist/event-subscriber.d.cts +116 -0
- package/dist/event-subscriber.d.ts +116 -0
- package/dist/event-subscriber.js +3 -0
- package/dist/event-subscriber.js.map +1 -0
- package/dist/event-testing.cjs +21 -0
- package/dist/event-testing.cjs.map +1 -0
- package/dist/event-testing.d.cts +110 -0
- package/dist/event-testing.d.ts +110 -0
- package/dist/event-testing.js +4 -0
- package/dist/event-testing.js.map +1 -0
- package/dist/event.cjs +30 -0
- package/dist/event.cjs.map +1 -0
- package/dist/event.d.cts +282 -0
- package/dist/event.d.ts +282 -0
- package/dist/event.js +13 -0
- package/dist/event.js.map +1 -0
- package/dist/exporters.cjs +17 -0
- package/dist/exporters.cjs.map +1 -0
- package/dist/exporters.d.cts +1 -0
- package/dist/exporters.d.ts +1 -0
- package/dist/exporters.js +4 -0
- package/dist/exporters.js.map +1 -0
- package/dist/functional.cjs +46 -0
- package/dist/functional.cjs.map +1 -0
- package/dist/functional.d.cts +478 -0
- package/dist/functional.d.ts +478 -0
- package/dist/functional.js +13 -0
- package/dist/functional.js.map +1 -0
- package/dist/http.cjs +189 -0
- package/dist/http.cjs.map +1 -0
- package/dist/http.d.cts +169 -0
- package/dist/http.d.ts +169 -0
- package/dist/http.js +184 -0
- package/dist/http.js.map +1 -0
- package/dist/index.cjs +333 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +758 -0
- package/dist/index.d.ts +758 -0
- package/dist/index.js +143 -0
- package/dist/index.js.map +1 -0
- package/dist/instrumentation.cjs +182 -0
- package/dist/instrumentation.cjs.map +1 -0
- package/dist/instrumentation.d.cts +49 -0
- package/dist/instrumentation.d.ts +49 -0
- package/dist/instrumentation.js +179 -0
- package/dist/instrumentation.js.map +1 -0
- package/dist/logger.cjs +19 -0
- package/dist/logger.cjs.map +1 -0
- package/dist/logger.d.cts +146 -0
- package/dist/logger.d.ts +146 -0
- package/dist/logger.js +6 -0
- package/dist/logger.js.map +1 -0
- package/dist/metric-helpers.cjs +31 -0
- package/dist/metric-helpers.cjs.map +1 -0
- package/dist/metric-helpers.d.cts +13 -0
- package/dist/metric-helpers.d.ts +13 -0
- package/dist/metric-helpers.js +6 -0
- package/dist/metric-helpers.js.map +1 -0
- package/dist/metric-testing.cjs +21 -0
- package/dist/metric-testing.cjs.map +1 -0
- package/dist/metric-testing.d.cts +110 -0
- package/dist/metric-testing.d.ts +110 -0
- package/dist/metric-testing.js +4 -0
- package/dist/metric-testing.js.map +1 -0
- package/dist/metric.cjs +26 -0
- package/dist/metric.cjs.map +1 -0
- package/dist/metric.d.cts +240 -0
- package/dist/metric.d.ts +240 -0
- package/dist/metric.js +9 -0
- package/dist/metric.js.map +1 -0
- package/dist/processors.cjs +17 -0
- package/dist/processors.cjs.map +1 -0
- package/dist/processors.d.cts +1 -0
- package/dist/processors.d.ts +1 -0
- package/dist/processors.js +4 -0
- package/dist/processors.js.map +1 -0
- package/dist/sampling.cjs +40 -0
- package/dist/sampling.cjs.map +1 -0
- package/dist/sampling.d.cts +260 -0
- package/dist/sampling.d.ts +260 -0
- package/dist/sampling.js +7 -0
- package/dist/sampling.js.map +1 -0
- package/dist/semantic-helpers.cjs +35 -0
- package/dist/semantic-helpers.cjs.map +1 -0
- package/dist/semantic-helpers.d.cts +442 -0
- package/dist/semantic-helpers.d.ts +442 -0
- package/dist/semantic-helpers.js +14 -0
- package/dist/semantic-helpers.js.map +1 -0
- package/dist/tail-sampling-processor.cjs +13 -0
- package/dist/tail-sampling-processor.cjs.map +1 -0
- package/dist/tail-sampling-processor.d.cts +27 -0
- package/dist/tail-sampling-processor.d.ts +27 -0
- package/dist/tail-sampling-processor.js +4 -0
- package/dist/tail-sampling-processor.js.map +1 -0
- package/dist/testing.cjs +286 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +291 -0
- package/dist/testing.d.ts +291 -0
- package/dist/testing.js +263 -0
- package/dist/testing.js.map +1 -0
- package/dist/trace-context-DRZdUvVY.d.cts +181 -0
- package/dist/trace-context-DRZdUvVY.d.ts +181 -0
- package/dist/trace-helpers.cjs +54 -0
- package/dist/trace-helpers.cjs.map +1 -0
- package/dist/trace-helpers.d.cts +524 -0
- package/dist/trace-helpers.d.ts +524 -0
- package/dist/trace-helpers.js +5 -0
- package/dist/trace-helpers.js.map +1 -0
- package/dist/tracer-provider.cjs +21 -0
- package/dist/tracer-provider.cjs.map +1 -0
- package/dist/tracer-provider.d.cts +169 -0
- package/dist/tracer-provider.d.ts +169 -0
- package/dist/tracer-provider.js +4 -0
- package/dist/tracer-provider.js.map +1 -0
- package/package.json +280 -0
- package/src/baggage-span-processor.test.ts +202 -0
- package/src/baggage-span-processor.ts +98 -0
- package/src/circuit-breaker.test.ts +341 -0
- package/src/circuit-breaker.ts +184 -0
- package/src/config.test.ts +94 -0
- package/src/config.ts +169 -0
- package/src/db.test.ts +252 -0
- package/src/db.ts +447 -0
- package/src/decorators.test.ts +203 -0
- package/src/decorators.ts +188 -0
- package/src/env-config.test.ts +246 -0
- package/src/env-config.ts +158 -0
- package/src/event-queue.test.ts +222 -0
- package/src/event-queue.ts +203 -0
- package/src/event-subscriber.ts +136 -0
- package/src/event-testing.ts +197 -0
- package/src/event.test.ts +718 -0
- package/src/event.ts +556 -0
- package/src/exporters.ts +96 -0
- package/src/functional.test.ts +1059 -0
- package/src/functional.ts +2295 -0
- package/src/http.test.ts +487 -0
- package/src/http.ts +424 -0
- package/src/index.ts +158 -0
- package/src/init.customization.test.ts +210 -0
- package/src/init.integrations.test.ts +366 -0
- package/src/init.openllmetry.test.ts +282 -0
- package/src/init.protocol.test.ts +215 -0
- package/src/init.ts +1426 -0
- package/src/instrumentation.test.ts +108 -0
- package/src/instrumentation.ts +308 -0
- package/src/logger.test.ts +117 -0
- package/src/logger.ts +246 -0
- package/src/metric-helpers.ts +47 -0
- package/src/metric-testing.ts +197 -0
- package/src/metric.ts +434 -0
- package/src/metrics.test.ts +205 -0
- package/src/operation-context.ts +93 -0
- package/src/processors.ts +106 -0
- package/src/rate-limiter.test.ts +199 -0
- package/src/rate-limiter.ts +98 -0
- package/src/sampling.test.ts +513 -0
- package/src/sampling.ts +428 -0
- package/src/semantic-helpers.test.ts +311 -0
- package/src/semantic-helpers.ts +584 -0
- package/src/shutdown.test.ts +311 -0
- package/src/shutdown.ts +222 -0
- package/src/stub.integration.test.ts +361 -0
- package/src/tail-sampling-processor.test.ts +226 -0
- package/src/tail-sampling-processor.ts +51 -0
- package/src/testing.ts +670 -0
- package/src/trace-context.ts +470 -0
- package/src/trace-helpers.new.test.ts +278 -0
- package/src/trace-helpers.test.ts +242 -0
- package/src/trace-helpers.ts +690 -0
- package/src/tracer-provider.test.ts +183 -0
- package/src/tracer-provider.ts +266 -0
- package/src/track.test.ts +153 -0
- package/src/track.ts +120 -0
- package/src/validation.test.ts +306 -0
- package/src/validation.ts +239 -0
- package/src/variable-name-inference.test.ts +178 -0
- package/src/variable-name-inference.ts +242 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import type { MetricReader } from '@opentelemetry/sdk-metrics';
|
|
3
|
+
import type { NodeSDK } from '@opentelemetry/sdk-node';
|
|
4
|
+
import type { SpanProcessor } from '@opentelemetry/sdk-trace-base';
|
|
5
|
+
import { mock, mockDeep, type DeepMockProxy } from 'vitest-mock-extended';
|
|
6
|
+
|
|
7
|
+
type SdkRecord = {
|
|
8
|
+
options: Record<string, unknown>;
|
|
9
|
+
instance: DeepMockProxy<NodeSDK>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const mockedModules = [
|
|
13
|
+
'@opentelemetry/sdk-node',
|
|
14
|
+
'@opentelemetry/exporter-trace-otlp-http',
|
|
15
|
+
'@opentelemetry/exporter-metrics-otlp-http',
|
|
16
|
+
'@opentelemetry/sdk-metrics',
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
async function loadInitWithMocks() {
|
|
20
|
+
const sdkInstances: SdkRecord[] = [];
|
|
21
|
+
const traceExporterOptions: Record<string, unknown>[] = [];
|
|
22
|
+
const metricExporterOptions: Record<string, unknown>[] = [];
|
|
23
|
+
const metricReaderOptions: Record<string, unknown>[] = [];
|
|
24
|
+
|
|
25
|
+
class MockNodeSDK {
|
|
26
|
+
constructor(options: Record<string, unknown>) {
|
|
27
|
+
const instance = mockDeep<NodeSDK>();
|
|
28
|
+
instance.start.mockImplementation(() => {});
|
|
29
|
+
instance.shutdown.mockResolvedValue();
|
|
30
|
+
sdkInstances.push({ options, instance });
|
|
31
|
+
return instance;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
class MockOTLPTraceExporter {
|
|
36
|
+
options: Record<string, unknown>;
|
|
37
|
+
|
|
38
|
+
constructor(options: Record<string, unknown>) {
|
|
39
|
+
this.options = options;
|
|
40
|
+
traceExporterOptions.push(options);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
class MockOTLPMetricExporter {
|
|
45
|
+
options: Record<string, unknown>;
|
|
46
|
+
|
|
47
|
+
constructor(options: Record<string, unknown>) {
|
|
48
|
+
this.options = options;
|
|
49
|
+
metricExporterOptions.push(options);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
class MockPeriodicExportingMetricReader {
|
|
54
|
+
options: Record<string, unknown>;
|
|
55
|
+
|
|
56
|
+
constructor(options: Record<string, unknown>) {
|
|
57
|
+
this.options = options;
|
|
58
|
+
metricReaderOptions.push(options);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
vi.doMock('@opentelemetry/sdk-node', () => ({
|
|
63
|
+
NodeSDK: MockNodeSDK,
|
|
64
|
+
}));
|
|
65
|
+
|
|
66
|
+
vi.doMock('@opentelemetry/exporter-trace-otlp-http', () => ({
|
|
67
|
+
OTLPTraceExporter: MockOTLPTraceExporter,
|
|
68
|
+
}));
|
|
69
|
+
|
|
70
|
+
vi.doMock('@opentelemetry/exporter-metrics-otlp-http', () => ({
|
|
71
|
+
OTLPMetricExporter: MockOTLPMetricExporter,
|
|
72
|
+
}));
|
|
73
|
+
|
|
74
|
+
vi.doMock('@opentelemetry/sdk-metrics', () => ({
|
|
75
|
+
PeriodicExportingMetricReader: MockPeriodicExportingMetricReader,
|
|
76
|
+
}));
|
|
77
|
+
|
|
78
|
+
const mod = await import('./init');
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
init: mod.init,
|
|
82
|
+
getConfig: mod.getConfig,
|
|
83
|
+
sdkInstances,
|
|
84
|
+
traceExporterOptions,
|
|
85
|
+
metricExporterOptions,
|
|
86
|
+
metricReaderOptions,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
describe('init() customization', () => {
|
|
91
|
+
beforeEach(() => {
|
|
92
|
+
vi.resetModules();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
afterEach(() => {
|
|
96
|
+
for (const mod of mockedModules) {
|
|
97
|
+
vi.doUnmock(mod);
|
|
98
|
+
}
|
|
99
|
+
vi.clearAllMocks();
|
|
100
|
+
delete process.env.AUTOTELEMETRY_METRICS;
|
|
101
|
+
delete process.env.NODE_ENV;
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('passes custom instrumentations to the NodeSDK', async () => {
|
|
105
|
+
const { init, sdkInstances } = await loadInitWithMocks();
|
|
106
|
+
|
|
107
|
+
const instrumentation = { name: 'http' } as any;
|
|
108
|
+
|
|
109
|
+
init({ service: 'instrumented-app', instrumentations: [instrumentation] });
|
|
110
|
+
|
|
111
|
+
const options = sdkInstances.at(-1)?.options as Record<string, unknown>;
|
|
112
|
+
expect(options.instrumentations).toBeDefined();
|
|
113
|
+
expect(options.instrumentations).toContain(instrumentation);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('merges resource attributes with defaults', async () => {
|
|
117
|
+
const { init, sdkInstances } = await loadInitWithMocks();
|
|
118
|
+
|
|
119
|
+
init({
|
|
120
|
+
service: 'resource-app',
|
|
121
|
+
resourceAttributes: { 'cloud.region': 'eu-central-1' },
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const resource = sdkInstances.at(-1)?.options.resource as {
|
|
125
|
+
attributes: Record<string, unknown>;
|
|
126
|
+
};
|
|
127
|
+
expect(resource.attributes['cloud.region']).toBe('eu-central-1');
|
|
128
|
+
expect(resource.attributes['service.name']).toBe('resource-app');
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('creates a default OTLP metric reader when metrics enabled', async () => {
|
|
132
|
+
const { init, metricReaderOptions, metricExporterOptions } =
|
|
133
|
+
await loadInitWithMocks();
|
|
134
|
+
|
|
135
|
+
init({ service: 'metrics-app', endpoint: 'http://localhost:4318' });
|
|
136
|
+
|
|
137
|
+
expect(metricReaderOptions).toHaveLength(1);
|
|
138
|
+
expect(metricExporterOptions).toHaveLength(1);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('skips default metric reader when metrics disabled', async () => {
|
|
142
|
+
const { init, metricReaderOptions } = await loadInitWithMocks();
|
|
143
|
+
|
|
144
|
+
init({ service: 'no-metrics', metrics: false });
|
|
145
|
+
|
|
146
|
+
expect(metricReaderOptions).toHaveLength(0);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('respects custom metric readers', async () => {
|
|
150
|
+
const { init, sdkInstances, metricReaderOptions } =
|
|
151
|
+
await loadInitWithMocks();
|
|
152
|
+
const customMetricReader = mock<MetricReader>();
|
|
153
|
+
|
|
154
|
+
init({ service: 'custom-metrics', metricReaders: [customMetricReader] });
|
|
155
|
+
|
|
156
|
+
const options = sdkInstances.at(-1)?.options as Record<string, unknown>;
|
|
157
|
+
expect(options.metricReaders).toEqual([customMetricReader]);
|
|
158
|
+
expect(metricReaderOptions).toHaveLength(0);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('applies OTLP headers for default exporters', async () => {
|
|
162
|
+
const { init, traceExporterOptions, metricExporterOptions } =
|
|
163
|
+
await loadInitWithMocks();
|
|
164
|
+
|
|
165
|
+
init({
|
|
166
|
+
service: 'headers-app',
|
|
167
|
+
endpoint: 'http://localhost:4318',
|
|
168
|
+
otlpHeaders: 'Authorization=Basic abc123',
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
expect(traceExporterOptions[0]).toMatchObject({
|
|
172
|
+
headers: { Authorization: 'Basic abc123' },
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
expect(metricExporterOptions[0]).toMatchObject({
|
|
176
|
+
headers: { Authorization: 'Basic abc123' },
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('supports sdkFactory overrides', async () => {
|
|
181
|
+
const { init, sdkInstances } = await loadInitWithMocks();
|
|
182
|
+
const customSdk = mockDeep<NodeSDK>();
|
|
183
|
+
customSdk.start.mockImplementation(() => {});
|
|
184
|
+
customSdk.shutdown.mockResolvedValue();
|
|
185
|
+
|
|
186
|
+
init({
|
|
187
|
+
service: 'custom-sdk',
|
|
188
|
+
endpoint: 'http://localhost:4318',
|
|
189
|
+
sdkFactory: (defaults) => {
|
|
190
|
+
expect(defaults.spanProcessors).toBeDefined();
|
|
191
|
+
return customSdk;
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
expect(sdkInstances).toHaveLength(0);
|
|
196
|
+
expect(customSdk.start).toHaveBeenCalled();
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('uses provided spanProcessors when supplied', async () => {
|
|
200
|
+
const { init, sdkInstances } = await loadInitWithMocks();
|
|
201
|
+
const customProcessor = mock<SpanProcessor>();
|
|
202
|
+
customProcessor.shutdown.mockResolvedValue();
|
|
203
|
+
customProcessor.forceFlush.mockResolvedValue();
|
|
204
|
+
|
|
205
|
+
init({ service: 'custom-span', spanProcessors: [customProcessor] });
|
|
206
|
+
|
|
207
|
+
const options = sdkInstances.at(-1)?.options as Record<string, unknown>;
|
|
208
|
+
expect(options.spanProcessors).toEqual([customProcessor]);
|
|
209
|
+
});
|
|
210
|
+
});
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import type { NodeSDK } from '@opentelemetry/sdk-node';
|
|
3
|
+
import type { DeepMockProxy } from 'vitest-mock-extended';
|
|
4
|
+
import { mockDeep } from 'vitest-mock-extended';
|
|
5
|
+
|
|
6
|
+
type SdkRecord = {
|
|
7
|
+
options: Record<string, unknown>;
|
|
8
|
+
instance: DeepMockProxy<NodeSDK>;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const mockedModules = [
|
|
12
|
+
'@opentelemetry/sdk-node',
|
|
13
|
+
'@opentelemetry/exporter-trace-otlp-http',
|
|
14
|
+
'@opentelemetry/exporter-metrics-otlp-http',
|
|
15
|
+
'@opentelemetry/sdk-metrics',
|
|
16
|
+
'@opentelemetry/auto-instrumentations-node',
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
// Mock instrumentation classes with exact names from OpenTelemetry
|
|
20
|
+
class MongoDBInstrumentation {
|
|
21
|
+
constructor(public config?: Record<string, unknown>) {}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
class MongooseInstrumentation {
|
|
25
|
+
constructor(public config?: Record<string, unknown>) {}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
class HttpInstrumentation {
|
|
29
|
+
constructor(public config?: Record<string, unknown>) {}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function loadInitWithMocks() {
|
|
33
|
+
const sdkInstances: SdkRecord[] = [];
|
|
34
|
+
const traceExporterOptions: Record<string, unknown>[] = [];
|
|
35
|
+
const metricExporterOptions: Record<string, unknown>[] = [];
|
|
36
|
+
const autoInstrumentationsConfig: Record<string, { enabled?: boolean }>[] =
|
|
37
|
+
[];
|
|
38
|
+
const logMessages: {
|
|
39
|
+
level: string;
|
|
40
|
+
message: string;
|
|
41
|
+
}[] = [];
|
|
42
|
+
|
|
43
|
+
class MockNodeSDK {
|
|
44
|
+
constructor(options: Record<string, unknown>) {
|
|
45
|
+
const instance = mockDeep<NodeSDK>();
|
|
46
|
+
instance.start.mockImplementation(() => {});
|
|
47
|
+
instance.shutdown.mockResolvedValue();
|
|
48
|
+
sdkInstances.push({ options, instance });
|
|
49
|
+
return instance;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
class MockOTLPTraceExporter {
|
|
54
|
+
options: Record<string, unknown>;
|
|
55
|
+
|
|
56
|
+
constructor(options: Record<string, unknown>) {
|
|
57
|
+
this.options = options;
|
|
58
|
+
traceExporterOptions.push(options);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
class MockOTLPMetricExporter {
|
|
63
|
+
options: Record<string, unknown>;
|
|
64
|
+
|
|
65
|
+
constructor(options: Record<string, unknown>) {
|
|
66
|
+
this.options = options;
|
|
67
|
+
metricExporterOptions.push(options);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
class MockPeriodicExportingMetricReader {
|
|
72
|
+
constructor(public options: Record<string, unknown>) {}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Mock getNodeAutoInstrumentations function
|
|
76
|
+
const mockGetNodeAutoInstrumentations = vi.fn(
|
|
77
|
+
(config?: Record<string, { enabled?: boolean }>) => {
|
|
78
|
+
if (config) {
|
|
79
|
+
autoInstrumentationsConfig.push(config);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Simulate returning auto-instrumentations based on config
|
|
83
|
+
const instrumentations: unknown[] = [];
|
|
84
|
+
|
|
85
|
+
// If MongoDB is not explicitly disabled, add it
|
|
86
|
+
if (
|
|
87
|
+
!config ||
|
|
88
|
+
!config['@opentelemetry/instrumentation-mongodb'] ||
|
|
89
|
+
config['@opentelemetry/instrumentation-mongodb'].enabled !== false
|
|
90
|
+
) {
|
|
91
|
+
instrumentations.push(new MongoDBInstrumentation());
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// If Mongoose is not explicitly disabled, add it
|
|
95
|
+
if (
|
|
96
|
+
!config ||
|
|
97
|
+
!config['@opentelemetry/instrumentation-mongoose'] ||
|
|
98
|
+
config['@opentelemetry/instrumentation-mongoose'].enabled !== false
|
|
99
|
+
) {
|
|
100
|
+
instrumentations.push(new MongooseInstrumentation());
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// If HTTP is not explicitly disabled, add it
|
|
104
|
+
if (
|
|
105
|
+
!config ||
|
|
106
|
+
!config['@opentelemetry/instrumentation-http'] ||
|
|
107
|
+
config['@opentelemetry/instrumentation-http'].enabled !== false
|
|
108
|
+
) {
|
|
109
|
+
instrumentations.push(new HttpInstrumentation());
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return instrumentations;
|
|
113
|
+
},
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
// Mock logger to capture log messages
|
|
117
|
+
const mockLogger = {
|
|
118
|
+
info: vi.fn((msg: string) => {
|
|
119
|
+
logMessages.push({ level: 'info', message: msg });
|
|
120
|
+
}),
|
|
121
|
+
warn: vi.fn((msg: string) => {
|
|
122
|
+
logMessages.push({ level: 'warn', message: msg });
|
|
123
|
+
}),
|
|
124
|
+
error: vi.fn((msg: string) => {
|
|
125
|
+
logMessages.push({ level: 'error', message: msg });
|
|
126
|
+
}),
|
|
127
|
+
debug: vi.fn((msg: string) => {
|
|
128
|
+
logMessages.push({ level: 'debug', message: msg });
|
|
129
|
+
}),
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
vi.doMock('@opentelemetry/sdk-node', () => ({
|
|
133
|
+
NodeSDK: MockNodeSDK,
|
|
134
|
+
}));
|
|
135
|
+
|
|
136
|
+
vi.doMock('@opentelemetry/exporter-trace-otlp-http', () => ({
|
|
137
|
+
OTLPTraceExporter: MockOTLPTraceExporter,
|
|
138
|
+
}));
|
|
139
|
+
|
|
140
|
+
vi.doMock('@opentelemetry/exporter-metrics-otlp-http', () => ({
|
|
141
|
+
OTLPMetricExporter: MockOTLPMetricExporter,
|
|
142
|
+
}));
|
|
143
|
+
|
|
144
|
+
vi.doMock('@opentelemetry/sdk-metrics', () => ({
|
|
145
|
+
PeriodicExportingMetricReader: MockPeriodicExportingMetricReader,
|
|
146
|
+
}));
|
|
147
|
+
|
|
148
|
+
vi.doMock('@opentelemetry/auto-instrumentations-node', () => ({
|
|
149
|
+
getNodeAutoInstrumentations: mockGetNodeAutoInstrumentations,
|
|
150
|
+
}));
|
|
151
|
+
|
|
152
|
+
const mod = await import('./init');
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
init: mod.init,
|
|
156
|
+
getConfig: mod.getConfig,
|
|
157
|
+
sdkInstances,
|
|
158
|
+
traceExporterOptions,
|
|
159
|
+
metricExporterOptions,
|
|
160
|
+
autoInstrumentationsConfig,
|
|
161
|
+
logMessages,
|
|
162
|
+
mockLogger,
|
|
163
|
+
mockGetNodeAutoInstrumentations,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
describe('init() integrations vs instrumentations', () => {
|
|
168
|
+
beforeEach(() => {
|
|
169
|
+
vi.resetModules();
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
afterEach(() => {
|
|
173
|
+
for (const mod of mockedModules) {
|
|
174
|
+
vi.doUnmock(mod);
|
|
175
|
+
}
|
|
176
|
+
vi.clearAllMocks();
|
|
177
|
+
delete process.env.AUTOTELEMETRY_METRICS;
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('excludes manual instrumentations from auto-instrumentations when integrations: true', async () => {
|
|
181
|
+
const {
|
|
182
|
+
init,
|
|
183
|
+
sdkInstances,
|
|
184
|
+
autoInstrumentationsConfig,
|
|
185
|
+
mockLogger,
|
|
186
|
+
logMessages,
|
|
187
|
+
} = await loadInitWithMocks();
|
|
188
|
+
|
|
189
|
+
const manualMongoDBInstrumentation = new MongoDBInstrumentation({
|
|
190
|
+
requireParentSpan: false,
|
|
191
|
+
});
|
|
192
|
+
const manualMongooseInstrumentation = new MongooseInstrumentation({
|
|
193
|
+
requireParentSpan: false,
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
init({
|
|
197
|
+
service: 'test-app',
|
|
198
|
+
integrations: true,
|
|
199
|
+
instrumentations: [
|
|
200
|
+
manualMongoDBInstrumentation,
|
|
201
|
+
manualMongooseInstrumentation,
|
|
202
|
+
],
|
|
203
|
+
logger: mockLogger,
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// Check that auto-instrumentations were called with exclusion config
|
|
207
|
+
expect(autoInstrumentationsConfig).toHaveLength(1);
|
|
208
|
+
const config = autoInstrumentationsConfig[0];
|
|
209
|
+
expect(config['@opentelemetry/instrumentation-mongodb']).toEqual({
|
|
210
|
+
enabled: false,
|
|
211
|
+
});
|
|
212
|
+
expect(config['@opentelemetry/instrumentation-mongoose']).toEqual({
|
|
213
|
+
enabled: false,
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// Check that manual instrumentations are in the final list
|
|
217
|
+
const options = sdkInstances.at(-1)?.options as Record<string, unknown>;
|
|
218
|
+
const instrumentations = options.instrumentations as unknown[];
|
|
219
|
+
expect(instrumentations).toContain(manualMongoDBInstrumentation);
|
|
220
|
+
expect(instrumentations).toContain(manualMongooseInstrumentation);
|
|
221
|
+
|
|
222
|
+
// Check that warning was logged
|
|
223
|
+
const infoMessages = logMessages.filter((log) => log.level === 'info');
|
|
224
|
+
expect(infoMessages).toHaveLength(1);
|
|
225
|
+
expect(infoMessages[0].message).toContain(
|
|
226
|
+
'Detected manual instrumentations',
|
|
227
|
+
);
|
|
228
|
+
expect(infoMessages[0].message).toContain('MongoDBInstrumentation');
|
|
229
|
+
expect(infoMessages[0].message).toContain('MongooseInstrumentation');
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('excludes manual instrumentations from specific auto-integrations list', async () => {
|
|
233
|
+
const {
|
|
234
|
+
init,
|
|
235
|
+
sdkInstances,
|
|
236
|
+
autoInstrumentationsConfig,
|
|
237
|
+
mockLogger,
|
|
238
|
+
logMessages,
|
|
239
|
+
} = await loadInitWithMocks();
|
|
240
|
+
|
|
241
|
+
const manualMongoDBInstrumentation = new MongoDBInstrumentation({
|
|
242
|
+
requireParentSpan: false,
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
init({
|
|
246
|
+
service: 'test-app',
|
|
247
|
+
integrations: ['http', 'mongodb'],
|
|
248
|
+
instrumentations: [manualMongoDBInstrumentation],
|
|
249
|
+
logger: mockLogger,
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// Check that auto-instrumentations were called with MongoDB disabled
|
|
253
|
+
expect(autoInstrumentationsConfig).toHaveLength(1);
|
|
254
|
+
const config = autoInstrumentationsConfig[0];
|
|
255
|
+
expect(config['@opentelemetry/instrumentation-mongodb']).toEqual({
|
|
256
|
+
enabled: false,
|
|
257
|
+
});
|
|
258
|
+
expect(config['@opentelemetry/instrumentation-http']).toEqual({
|
|
259
|
+
enabled: true,
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// Check that manual MongoDB instrumentation is in the final list
|
|
263
|
+
const options = sdkInstances.at(-1)?.options as Record<string, unknown>;
|
|
264
|
+
const instrumentations = options.instrumentations as unknown[];
|
|
265
|
+
expect(instrumentations).toContain(manualMongoDBInstrumentation);
|
|
266
|
+
|
|
267
|
+
// Check that warning was logged
|
|
268
|
+
const infoMessages = logMessages.filter((log) => log.level === 'info');
|
|
269
|
+
expect(infoMessages).toHaveLength(1);
|
|
270
|
+
expect(infoMessages[0].message).toContain('MongoDBInstrumentation');
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it('does not log warning when no manual instrumentations provided', async () => {
|
|
274
|
+
const { init, mockLogger, logMessages } = await loadInitWithMocks();
|
|
275
|
+
|
|
276
|
+
init({
|
|
277
|
+
service: 'test-app',
|
|
278
|
+
integrations: true,
|
|
279
|
+
logger: mockLogger,
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
// Check that no warning was logged
|
|
283
|
+
const infoMessages = logMessages.filter(
|
|
284
|
+
(log) => log.level === 'info' && log.message.includes('Detected manual'),
|
|
285
|
+
);
|
|
286
|
+
expect(infoMessages).toHaveLength(0);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it('does not log warning when integrations is false', async () => {
|
|
290
|
+
const { init, mockLogger, logMessages } = await loadInitWithMocks();
|
|
291
|
+
|
|
292
|
+
const manualMongoDBInstrumentation = new MongoDBInstrumentation({
|
|
293
|
+
requireParentSpan: false,
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
init({
|
|
297
|
+
service: 'test-app',
|
|
298
|
+
integrations: false,
|
|
299
|
+
instrumentations: [manualMongoDBInstrumentation],
|
|
300
|
+
logger: mockLogger,
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
// Check that no warning was logged
|
|
304
|
+
const infoMessages = logMessages.filter(
|
|
305
|
+
(log) => log.level === 'info' && log.message.includes('Detected manual'),
|
|
306
|
+
);
|
|
307
|
+
expect(infoMessages).toHaveLength(0);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it('handles object-style integrations config with manual instrumentations', async () => {
|
|
311
|
+
const { init, sdkInstances, autoInstrumentationsConfig, mockLogger } =
|
|
312
|
+
await loadInitWithMocks();
|
|
313
|
+
|
|
314
|
+
const manualMongoDBInstrumentation = new MongoDBInstrumentation({
|
|
315
|
+
requireParentSpan: false,
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
init({
|
|
319
|
+
service: 'test-app',
|
|
320
|
+
integrations: {
|
|
321
|
+
http: { enabled: true },
|
|
322
|
+
mongodb: { enabled: true },
|
|
323
|
+
},
|
|
324
|
+
instrumentations: [manualMongoDBInstrumentation],
|
|
325
|
+
logger: mockLogger,
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
// Check that auto-instrumentations were called with MongoDB disabled
|
|
329
|
+
expect(autoInstrumentationsConfig).toHaveLength(1);
|
|
330
|
+
const config = autoInstrumentationsConfig[0];
|
|
331
|
+
expect(config['@opentelemetry/instrumentation-mongodb']).toEqual({
|
|
332
|
+
enabled: false,
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
// Check that manual MongoDB instrumentation is in the final list
|
|
336
|
+
const options = sdkInstances.at(-1)?.options as Record<string, unknown>;
|
|
337
|
+
const instrumentations = options.instrumentations as unknown[];
|
|
338
|
+
expect(instrumentations).toContain(manualMongoDBInstrumentation);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it('works correctly when no conflicts exist', async () => {
|
|
342
|
+
const { init, sdkInstances, mockLogger, logMessages } =
|
|
343
|
+
await loadInitWithMocks();
|
|
344
|
+
|
|
345
|
+
const manualHttpInstrumentation = new HttpInstrumentation({
|
|
346
|
+
requireParentSpan: false,
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
init({
|
|
350
|
+
service: 'test-app',
|
|
351
|
+
integrations: ['mongodb', 'mongoose'],
|
|
352
|
+
instrumentations: [manualHttpInstrumentation],
|
|
353
|
+
logger: mockLogger,
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
// Check that manual instrumentation is in the final list
|
|
357
|
+
const options = sdkInstances.at(-1)?.options as Record<string, unknown>;
|
|
358
|
+
const instrumentations = options.instrumentations as unknown[];
|
|
359
|
+
expect(instrumentations).toContain(manualHttpInstrumentation);
|
|
360
|
+
|
|
361
|
+
// Check that warning was logged (because manual HTTP provided with auto-integrations)
|
|
362
|
+
const infoMessages = logMessages.filter((log) => log.level === 'info');
|
|
363
|
+
expect(infoMessages).toHaveLength(1);
|
|
364
|
+
expect(infoMessages[0].message).toContain('HttpInstrumentation');
|
|
365
|
+
});
|
|
366
|
+
});
|