observability-toolkit 1.8.5 → 2.0.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 +126 -5
- package/dist/backends/index.d.ts +163 -0
- package/dist/backends/index.d.ts.map +1 -1
- package/dist/backends/index.js +57 -0
- package/dist/backends/index.js.map +1 -1
- package/dist/backends/index.test.js +55 -1
- package/dist/backends/index.test.js.map +1 -1
- package/dist/backends/local-jsonl.d.ts +30 -0
- package/dist/backends/local-jsonl.d.ts.map +1 -1
- package/dist/backends/local-jsonl.js +912 -550
- package/dist/backends/local-jsonl.js.map +1 -1
- package/dist/backends/signoz-api-rate-limiter.test.js +2 -1
- package/dist/backends/signoz-api-rate-limiter.test.js.map +1 -1
- package/dist/backends/signoz-api.d.ts +16 -2
- package/dist/backends/signoz-api.d.ts.map +1 -1
- package/dist/backends/signoz-api.js +650 -534
- package/dist/backends/signoz-api.js.map +1 -1
- package/dist/backends/signoz-api.test.js +6 -5
- package/dist/backends/signoz-api.test.js.map +1 -1
- package/dist/lib/agent-as-judge.d.ts +388 -0
- package/dist/lib/agent-as-judge.d.ts.map +1 -0
- package/dist/lib/agent-as-judge.js +740 -0
- package/dist/lib/agent-as-judge.js.map +1 -0
- package/dist/lib/agent-as-judge.test.d.ts +5 -0
- package/dist/lib/agent-as-judge.test.d.ts.map +1 -0
- package/dist/lib/agent-as-judge.test.js +816 -0
- package/dist/lib/agent-as-judge.test.js.map +1 -0
- package/dist/lib/cache.d.ts +15 -2
- package/dist/lib/cache.d.ts.map +1 -1
- package/dist/lib/cache.js +16 -2
- package/dist/lib/cache.js.map +1 -1
- package/dist/lib/circuit-breaker.d.ts +18 -0
- package/dist/lib/circuit-breaker.d.ts.map +1 -1
- package/dist/lib/circuit-breaker.js +41 -8
- package/dist/lib/circuit-breaker.js.map +1 -1
- package/dist/lib/confident-export.d.ts +101 -0
- package/dist/lib/confident-export.d.ts.map +1 -0
- package/dist/lib/confident-export.js +393 -0
- package/dist/lib/confident-export.js.map +1 -0
- package/dist/lib/confident-export.test.d.ts +7 -0
- package/dist/lib/confident-export.test.d.ts.map +1 -0
- package/dist/lib/confident-export.test.js +835 -0
- package/dist/lib/confident-export.test.js.map +1 -0
- package/dist/lib/constants.d.ts +75 -0
- package/dist/lib/constants.d.ts.map +1 -1
- package/dist/lib/constants.js +104 -1
- package/dist/lib/constants.js.map +1 -1
- package/dist/lib/datadog-export.d.ts +156 -0
- package/dist/lib/datadog-export.d.ts.map +1 -0
- package/dist/lib/datadog-export.js +464 -0
- package/dist/lib/datadog-export.js.map +1 -0
- package/dist/lib/datadog-export.test.d.ts +14 -0
- package/dist/lib/datadog-export.test.d.ts.map +1 -0
- package/dist/lib/datadog-export.test.js +890 -0
- package/dist/lib/datadog-export.test.js.map +1 -0
- package/dist/lib/evaluation-hooks.d.ts +49 -0
- package/dist/lib/evaluation-hooks.d.ts.map +1 -0
- package/dist/lib/evaluation-hooks.js +488 -0
- package/dist/lib/evaluation-hooks.js.map +1 -0
- package/dist/lib/evaluation-hooks.test.d.ts +8 -0
- package/dist/lib/evaluation-hooks.test.d.ts.map +1 -0
- package/dist/lib/evaluation-hooks.test.js +624 -0
- package/dist/lib/evaluation-hooks.test.js.map +1 -0
- package/dist/lib/export-utils.d.ts +99 -0
- package/dist/lib/export-utils.d.ts.map +1 -0
- package/dist/lib/export-utils.js +238 -0
- package/dist/lib/export-utils.js.map +1 -0
- package/dist/lib/export-utils.test.d.ts +5 -0
- package/dist/lib/export-utils.test.d.ts.map +1 -0
- package/dist/lib/export-utils.test.js +193 -0
- package/dist/lib/export-utils.test.js.map +1 -0
- package/dist/lib/file-utils.d.ts +17 -2
- package/dist/lib/file-utils.d.ts.map +1 -1
- package/dist/lib/file-utils.js +24 -5
- package/dist/lib/file-utils.js.map +1 -1
- package/dist/lib/file-utils.test.js +30 -0
- package/dist/lib/file-utils.test.js.map +1 -1
- package/dist/lib/histogram.d.ts +119 -0
- package/dist/lib/histogram.d.ts.map +1 -0
- package/dist/lib/histogram.js +202 -0
- package/dist/lib/histogram.js.map +1 -0
- package/dist/lib/histogram.test.d.ts +5 -0
- package/dist/lib/histogram.test.d.ts.map +1 -0
- package/dist/lib/histogram.test.js +381 -0
- package/dist/lib/histogram.test.js.map +1 -0
- package/dist/lib/instrumentation.d.ts +153 -0
- package/dist/lib/instrumentation.d.ts.map +1 -0
- package/dist/lib/instrumentation.integration.test.d.ts +2 -0
- package/dist/lib/instrumentation.integration.test.d.ts.map +1 -0
- package/dist/lib/instrumentation.integration.test.js +589 -0
- package/dist/lib/instrumentation.integration.test.js.map +1 -0
- package/dist/lib/instrumentation.js +520 -0
- package/dist/lib/instrumentation.js.map +1 -0
- package/dist/lib/instrumentation.test.d.ts +2 -0
- package/dist/lib/instrumentation.test.d.ts.map +1 -0
- package/dist/lib/instrumentation.test.js +821 -0
- package/dist/lib/instrumentation.test.js.map +1 -0
- package/dist/lib/langfuse-export.d.ts +125 -0
- package/dist/lib/langfuse-export.d.ts.map +1 -0
- package/dist/lib/langfuse-export.js +367 -0
- package/dist/lib/langfuse-export.js.map +1 -0
- package/dist/lib/langfuse-export.test.d.ts +7 -0
- package/dist/lib/langfuse-export.test.d.ts.map +1 -0
- package/dist/lib/langfuse-export.test.js +1007 -0
- package/dist/lib/langfuse-export.test.js.map +1 -0
- package/dist/lib/llm-as-judge.d.ts +657 -0
- package/dist/lib/llm-as-judge.d.ts.map +1 -0
- package/dist/lib/llm-as-judge.js +1397 -0
- package/dist/lib/llm-as-judge.js.map +1 -0
- package/dist/lib/llm-as-judge.test.d.ts +2 -0
- package/dist/lib/llm-as-judge.test.d.ts.map +1 -0
- package/dist/lib/llm-as-judge.test.js +2409 -0
- package/dist/lib/llm-as-judge.test.js.map +1 -0
- package/dist/lib/logger.d.ts +1 -1
- package/dist/lib/logger.d.ts.map +1 -1
- package/dist/lib/logger.js.map +1 -1
- package/dist/lib/metrics.d.ts +62 -0
- package/dist/lib/metrics.d.ts.map +1 -0
- package/dist/lib/metrics.js +166 -0
- package/dist/lib/metrics.js.map +1 -0
- package/dist/lib/metrics.test.d.ts +5 -0
- package/dist/lib/metrics.test.d.ts.map +1 -0
- package/dist/lib/metrics.test.js +189 -0
- package/dist/lib/metrics.test.js.map +1 -0
- package/dist/lib/parse-stats.d.ts +119 -0
- package/dist/lib/parse-stats.d.ts.map +1 -0
- package/dist/lib/parse-stats.js +206 -0
- package/dist/lib/parse-stats.js.map +1 -0
- package/dist/lib/parse-stats.test.d.ts +5 -0
- package/dist/lib/parse-stats.test.d.ts.map +1 -0
- package/dist/lib/parse-stats.test.js +283 -0
- package/dist/lib/parse-stats.test.js.map +1 -0
- package/dist/lib/phoenix-export.d.ts +109 -0
- package/dist/lib/phoenix-export.d.ts.map +1 -0
- package/dist/lib/phoenix-export.js +429 -0
- package/dist/lib/phoenix-export.js.map +1 -0
- package/dist/lib/phoenix-export.test.d.ts +11 -0
- package/dist/lib/phoenix-export.test.d.ts.map +1 -0
- package/dist/lib/phoenix-export.test.js +725 -0
- package/dist/lib/phoenix-export.test.js.map +1 -0
- package/dist/lib/server-utils.d.ts +6 -1
- package/dist/lib/server-utils.d.ts.map +1 -1
- package/dist/lib/server-utils.js +9 -1
- package/dist/lib/server-utils.js.map +1 -1
- package/dist/lib/shared-schemas.d.ts +6 -0
- package/dist/lib/shared-schemas.d.ts.map +1 -1
- package/dist/lib/shared-schemas.js +11 -4
- package/dist/lib/shared-schemas.js.map +1 -1
- package/dist/lib/verification-events.d.ts +100 -0
- package/dist/lib/verification-events.d.ts.map +1 -0
- package/dist/lib/verification-events.js +162 -0
- package/dist/lib/verification-events.js.map +1 -0
- package/dist/lib/verification-events.test.d.ts +5 -0
- package/dist/lib/verification-events.test.d.ts.map +1 -0
- package/dist/lib/verification-events.test.js +193 -0
- package/dist/lib/verification-events.test.js.map +1 -0
- package/dist/server.d.ts +5 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +77 -21
- package/dist/server.js.map +1 -1
- package/dist/tools/context-stats.d.ts.map +1 -1
- package/dist/tools/context-stats.js +6 -8
- package/dist/tools/context-stats.js.map +1 -1
- package/dist/tools/export-confident.d.ts +145 -0
- package/dist/tools/export-confident.d.ts.map +1 -0
- package/dist/tools/export-confident.js +134 -0
- package/dist/tools/export-confident.js.map +1 -0
- package/dist/tools/export-confident.test.d.ts +7 -0
- package/dist/tools/export-confident.test.d.ts.map +1 -0
- package/dist/tools/export-confident.test.js +332 -0
- package/dist/tools/export-confident.test.js.map +1 -0
- package/dist/tools/export-datadog.d.ts +160 -0
- package/dist/tools/export-datadog.d.ts.map +1 -0
- package/dist/tools/export-datadog.js +160 -0
- package/dist/tools/export-datadog.js.map +1 -0
- package/dist/tools/export-datadog.test.d.ts +8 -0
- package/dist/tools/export-datadog.test.d.ts.map +1 -0
- package/dist/tools/export-datadog.test.js +419 -0
- package/dist/tools/export-datadog.test.js.map +1 -0
- package/dist/tools/export-langfuse.d.ts +137 -0
- package/dist/tools/export-langfuse.d.ts.map +1 -0
- package/dist/tools/export-langfuse.js +131 -0
- package/dist/tools/export-langfuse.js.map +1 -0
- package/dist/tools/export-langfuse.test.d.ts +7 -0
- package/dist/tools/export-langfuse.test.d.ts.map +1 -0
- package/dist/tools/export-langfuse.test.js +303 -0
- package/dist/tools/export-langfuse.test.js.map +1 -0
- package/dist/tools/export-phoenix.d.ts +145 -0
- package/dist/tools/export-phoenix.d.ts.map +1 -0
- package/dist/tools/export-phoenix.js +135 -0
- package/dist/tools/export-phoenix.js.map +1 -0
- package/dist/tools/export-phoenix.test.d.ts +7 -0
- package/dist/tools/export-phoenix.test.d.ts.map +1 -0
- package/dist/tools/export-phoenix.test.js +316 -0
- package/dist/tools/export-phoenix.test.js.map +1 -0
- package/dist/tools/health-check.d.ts +26 -0
- package/dist/tools/health-check.d.ts.map +1 -1
- package/dist/tools/health-check.js +36 -7
- package/dist/tools/health-check.js.map +1 -1
- package/dist/tools/index.d.ts +6 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +6 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/inject-evaluations.d.ts +1315 -0
- package/dist/tools/inject-evaluations.d.ts.map +1 -0
- package/dist/tools/inject-evaluations.js +121 -0
- package/dist/tools/inject-evaluations.js.map +1 -0
- package/dist/tools/inject-evaluations.test.d.ts +5 -0
- package/dist/tools/inject-evaluations.test.d.ts.map +1 -0
- package/dist/tools/inject-evaluations.test.js +359 -0
- package/dist/tools/inject-evaluations.test.js.map +1 -0
- package/dist/tools/query-evaluations.d.ts +25 -4
- package/dist/tools/query-evaluations.d.ts.map +1 -1
- package/dist/tools/query-evaluations.js +10 -0
- package/dist/tools/query-evaluations.js.map +1 -1
- package/dist/tools/query-llm-events.js +2 -2
- package/dist/tools/query-llm-events.js.map +1 -1
- package/dist/tools/query-logs.d.ts +8 -8
- package/dist/tools/query-logs.js +3 -3
- package/dist/tools/query-logs.js.map +1 -1
- package/dist/tools/query-metrics.d.ts +4 -4
- package/dist/tools/query-metrics.js +2 -2
- package/dist/tools/query-metrics.js.map +1 -1
- package/dist/tools/query-traces.d.ts +8 -8
- package/dist/tools/query-verifications.d.ts +111 -0
- package/dist/tools/query-verifications.d.ts.map +1 -0
- package/dist/tools/query-verifications.js +101 -0
- package/dist/tools/query-verifications.js.map +1 -0
- package/dist/tools/query-verifications.test.d.ts +5 -0
- package/dist/tools/query-verifications.test.d.ts.map +1 -0
- package/dist/tools/query-verifications.test.js +156 -0
- package/dist/tools/query-verifications.test.js.map +1 -0
- package/dist/types/evaluation-hooks.d.ts +176 -0
- package/dist/types/evaluation-hooks.d.ts.map +1 -0
- package/dist/types/evaluation-hooks.js +49 -0
- package/dist/types/evaluation-hooks.js.map +1 -0
- package/package.json +10 -2
|
@@ -0,0 +1,821 @@
|
|
|
1
|
+
import { describe, it, before, after, beforeEach, afterEach } from 'node:test';
|
|
2
|
+
import * as assert from 'node:assert';
|
|
3
|
+
import { initializeInstrumentation, shutdownInstrumentation, resetInstrumentationForTesting, getTracer, isInstrumentationEnabled, withSpan, withSpanSync, waitForInitialization, getInitializationStatus, InstrumentationManager, getDefaultManager, } from './instrumentation.js';
|
|
4
|
+
describe('Instrumentation', () => {
|
|
5
|
+
// Store original env vars
|
|
6
|
+
let originalOtelEnabled;
|
|
7
|
+
let originalOtelEndpoint;
|
|
8
|
+
let originalOtelServiceName;
|
|
9
|
+
before(() => {
|
|
10
|
+
originalOtelEnabled = process.env.OTEL_ENABLED;
|
|
11
|
+
originalOtelEndpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT;
|
|
12
|
+
originalOtelServiceName = process.env.OTEL_SERVICE_NAME;
|
|
13
|
+
});
|
|
14
|
+
afterEach(async () => {
|
|
15
|
+
// Reset env vars after each test
|
|
16
|
+
if (originalOtelEnabled !== undefined) {
|
|
17
|
+
process.env.OTEL_ENABLED = originalOtelEnabled;
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
delete process.env.OTEL_ENABLED;
|
|
21
|
+
}
|
|
22
|
+
if (originalOtelEndpoint !== undefined) {
|
|
23
|
+
process.env.OTEL_EXPORTER_OTLP_ENDPOINT = originalOtelEndpoint;
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
delete process.env.OTEL_EXPORTER_OTLP_ENDPOINT;
|
|
27
|
+
}
|
|
28
|
+
if (originalOtelServiceName !== undefined) {
|
|
29
|
+
process.env.OTEL_SERVICE_NAME = originalOtelServiceName;
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
delete process.env.OTEL_SERVICE_NAME;
|
|
33
|
+
}
|
|
34
|
+
// Always reset instrumentation state
|
|
35
|
+
await shutdownInstrumentation();
|
|
36
|
+
resetInstrumentationForTesting();
|
|
37
|
+
});
|
|
38
|
+
after(() => {
|
|
39
|
+
// Restore original env vars
|
|
40
|
+
if (originalOtelEnabled !== undefined) {
|
|
41
|
+
process.env.OTEL_ENABLED = originalOtelEnabled;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
delete process.env.OTEL_ENABLED;
|
|
45
|
+
}
|
|
46
|
+
if (originalOtelEndpoint !== undefined) {
|
|
47
|
+
process.env.OTEL_EXPORTER_OTLP_ENDPOINT = originalOtelEndpoint;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
delete process.env.OTEL_EXPORTER_OTLP_ENDPOINT;
|
|
51
|
+
}
|
|
52
|
+
if (originalOtelServiceName !== undefined) {
|
|
53
|
+
process.env.OTEL_SERVICE_NAME = originalOtelServiceName;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
delete process.env.OTEL_SERVICE_NAME;
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
describe('isInstrumentationEnabled', () => {
|
|
60
|
+
it('should return false when OTEL_ENABLED is not set', () => {
|
|
61
|
+
delete process.env.OTEL_ENABLED;
|
|
62
|
+
assert.strictEqual(isInstrumentationEnabled(), false);
|
|
63
|
+
});
|
|
64
|
+
it('should return false when OTEL_ENABLED is "false"', () => {
|
|
65
|
+
process.env.OTEL_ENABLED = 'false';
|
|
66
|
+
assert.strictEqual(isInstrumentationEnabled(), false);
|
|
67
|
+
});
|
|
68
|
+
it('should return true when OTEL_ENABLED is "true"', () => {
|
|
69
|
+
process.env.OTEL_ENABLED = 'true';
|
|
70
|
+
assert.strictEqual(isInstrumentationEnabled(), true);
|
|
71
|
+
});
|
|
72
|
+
it('should respect config.enabled over environment', () => {
|
|
73
|
+
process.env.OTEL_ENABLED = 'true';
|
|
74
|
+
assert.strictEqual(isInstrumentationEnabled({ enabled: false }), false);
|
|
75
|
+
process.env.OTEL_ENABLED = 'false';
|
|
76
|
+
assert.strictEqual(isInstrumentationEnabled({ enabled: true }), true);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
describe('initializeInstrumentation - disabled', () => {
|
|
80
|
+
it('should skip initialization when disabled (default)', async () => {
|
|
81
|
+
delete process.env.OTEL_ENABLED;
|
|
82
|
+
await initializeInstrumentation();
|
|
83
|
+
assert.strictEqual(getTracer(), null);
|
|
84
|
+
});
|
|
85
|
+
it('should skip initialization when OTEL_ENABLED is false', async () => {
|
|
86
|
+
process.env.OTEL_ENABLED = 'false';
|
|
87
|
+
process.env.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://localhost:4318';
|
|
88
|
+
await initializeInstrumentation();
|
|
89
|
+
assert.strictEqual(getTracer(), null);
|
|
90
|
+
});
|
|
91
|
+
it('should skip initialization when enabled but no endpoint', async () => {
|
|
92
|
+
process.env.OTEL_ENABLED = 'true';
|
|
93
|
+
delete process.env.OTEL_EXPORTER_OTLP_ENDPOINT;
|
|
94
|
+
await initializeInstrumentation();
|
|
95
|
+
assert.strictEqual(getTracer(), null);
|
|
96
|
+
});
|
|
97
|
+
it('should skip when config overrides to disabled', async () => {
|
|
98
|
+
process.env.OTEL_ENABLED = 'true';
|
|
99
|
+
process.env.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://localhost:4318';
|
|
100
|
+
await initializeInstrumentation({ enabled: false });
|
|
101
|
+
assert.strictEqual(getTracer(), null);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
describe('initializeInstrumentation - enabled', () => {
|
|
105
|
+
it('should initialize when enabled via environment', async () => {
|
|
106
|
+
process.env.OTEL_ENABLED = 'true';
|
|
107
|
+
process.env.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://localhost:4318';
|
|
108
|
+
await initializeInstrumentation();
|
|
109
|
+
// Tracer should be set (not null)
|
|
110
|
+
assert.notStrictEqual(getTracer(), null);
|
|
111
|
+
});
|
|
112
|
+
it('should initialize when enabled via config', async () => {
|
|
113
|
+
await initializeInstrumentation({
|
|
114
|
+
enabled: true,
|
|
115
|
+
endpoint: 'http://localhost:4318',
|
|
116
|
+
});
|
|
117
|
+
assert.notStrictEqual(getTracer(), null);
|
|
118
|
+
});
|
|
119
|
+
it('should use custom service name from config', async () => {
|
|
120
|
+
await initializeInstrumentation({
|
|
121
|
+
enabled: true,
|
|
122
|
+
endpoint: 'http://localhost:4318',
|
|
123
|
+
serviceName: 'custom-service',
|
|
124
|
+
});
|
|
125
|
+
assert.notStrictEqual(getTracer(), null);
|
|
126
|
+
});
|
|
127
|
+
it('should use OTEL_SERVICE_NAME environment variable', async () => {
|
|
128
|
+
process.env.OTEL_ENABLED = 'true';
|
|
129
|
+
process.env.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://localhost:4318';
|
|
130
|
+
process.env.OTEL_SERVICE_NAME = 'env-service';
|
|
131
|
+
await initializeInstrumentation();
|
|
132
|
+
assert.notStrictEqual(getTracer(), null);
|
|
133
|
+
});
|
|
134
|
+
it('should be idempotent (multiple calls are no-ops)', async () => {
|
|
135
|
+
await initializeInstrumentation({
|
|
136
|
+
enabled: true,
|
|
137
|
+
endpoint: 'http://localhost:4318',
|
|
138
|
+
});
|
|
139
|
+
const tracer1 = getTracer();
|
|
140
|
+
await initializeInstrumentation({
|
|
141
|
+
enabled: true,
|
|
142
|
+
endpoint: 'http://localhost:4318',
|
|
143
|
+
});
|
|
144
|
+
const tracer2 = getTracer();
|
|
145
|
+
// Should be the same tracer (not re-initialized)
|
|
146
|
+
assert.strictEqual(tracer1, tracer2);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
describe('getTracer', () => {
|
|
150
|
+
it('should return null when not initialized', () => {
|
|
151
|
+
assert.strictEqual(getTracer(), null);
|
|
152
|
+
});
|
|
153
|
+
it('should return null when disabled', async () => {
|
|
154
|
+
await initializeInstrumentation({ enabled: false });
|
|
155
|
+
assert.strictEqual(getTracer(), null);
|
|
156
|
+
});
|
|
157
|
+
it('should return tracer when enabled', async () => {
|
|
158
|
+
await initializeInstrumentation({
|
|
159
|
+
enabled: true,
|
|
160
|
+
endpoint: 'http://localhost:4318',
|
|
161
|
+
});
|
|
162
|
+
const tracer = getTracer();
|
|
163
|
+
assert.notStrictEqual(tracer, null);
|
|
164
|
+
assert.ok(typeof tracer?.startSpan === 'function');
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
describe('shutdownInstrumentation', () => {
|
|
168
|
+
it('should be safe to call when not initialized', async () => {
|
|
169
|
+
await assert.doesNotReject(async () => {
|
|
170
|
+
await shutdownInstrumentation();
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
it('should be safe to call multiple times', async () => {
|
|
174
|
+
await initializeInstrumentation({
|
|
175
|
+
enabled: true,
|
|
176
|
+
endpoint: 'http://localhost:4318',
|
|
177
|
+
});
|
|
178
|
+
await assert.doesNotReject(async () => {
|
|
179
|
+
await shutdownInstrumentation();
|
|
180
|
+
await shutdownInstrumentation();
|
|
181
|
+
await shutdownInstrumentation();
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
it('should clear tracer on shutdown', async () => {
|
|
185
|
+
await initializeInstrumentation({
|
|
186
|
+
enabled: true,
|
|
187
|
+
endpoint: 'http://localhost:4318',
|
|
188
|
+
});
|
|
189
|
+
assert.notStrictEqual(getTracer(), null);
|
|
190
|
+
await shutdownInstrumentation();
|
|
191
|
+
assert.strictEqual(getTracer(), null);
|
|
192
|
+
});
|
|
193
|
+
it('should allow re-initialization after shutdown', async () => {
|
|
194
|
+
await initializeInstrumentation({
|
|
195
|
+
enabled: true,
|
|
196
|
+
endpoint: 'http://localhost:4318',
|
|
197
|
+
});
|
|
198
|
+
await shutdownInstrumentation();
|
|
199
|
+
resetInstrumentationForTesting();
|
|
200
|
+
await initializeInstrumentation({
|
|
201
|
+
enabled: true,
|
|
202
|
+
endpoint: 'http://localhost:4318',
|
|
203
|
+
});
|
|
204
|
+
assert.notStrictEqual(getTracer(), null);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
describe('resetInstrumentationForTesting', () => {
|
|
208
|
+
it('should reset all internal state', async () => {
|
|
209
|
+
await initializeInstrumentation({
|
|
210
|
+
enabled: true,
|
|
211
|
+
endpoint: 'http://localhost:4318',
|
|
212
|
+
});
|
|
213
|
+
assert.notStrictEqual(getTracer(), null);
|
|
214
|
+
resetInstrumentationForTesting();
|
|
215
|
+
assert.strictEqual(getTracer(), null);
|
|
216
|
+
});
|
|
217
|
+
it('should allow fresh initialization after reset', async () => {
|
|
218
|
+
await initializeInstrumentation({
|
|
219
|
+
enabled: true,
|
|
220
|
+
endpoint: 'http://localhost:4318',
|
|
221
|
+
});
|
|
222
|
+
resetInstrumentationForTesting();
|
|
223
|
+
// Should be able to initialize again (unlike shutdown which sets initialized=false)
|
|
224
|
+
await initializeInstrumentation({
|
|
225
|
+
enabled: true,
|
|
226
|
+
endpoint: 'http://localhost:4318',
|
|
227
|
+
});
|
|
228
|
+
assert.notStrictEqual(getTracer(), null);
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
describe('withSpan - disabled', () => {
|
|
232
|
+
it('should execute function directly when disabled', async () => {
|
|
233
|
+
const result = await withSpan('test.span', { attr: 'value' }, async (span) => {
|
|
234
|
+
assert.strictEqual(span, null);
|
|
235
|
+
return 'result';
|
|
236
|
+
});
|
|
237
|
+
assert.strictEqual(result, 'result');
|
|
238
|
+
});
|
|
239
|
+
it('should propagate errors when disabled', async () => {
|
|
240
|
+
const error = new Error('test error');
|
|
241
|
+
await assert.rejects(async () => {
|
|
242
|
+
await withSpan('test.span', {}, async () => {
|
|
243
|
+
throw error;
|
|
244
|
+
});
|
|
245
|
+
}, (err) => {
|
|
246
|
+
assert.strictEqual(err, error);
|
|
247
|
+
return true;
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
it('should handle undefined attributes when disabled', async () => {
|
|
251
|
+
const result = await withSpan('test.span', { defined: 'value', undef: undefined }, async () => 'ok');
|
|
252
|
+
assert.strictEqual(result, 'ok');
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
describe('withSpan - enabled', () => {
|
|
256
|
+
beforeEach(async () => {
|
|
257
|
+
await initializeInstrumentation({
|
|
258
|
+
enabled: true,
|
|
259
|
+
endpoint: 'http://localhost:4318',
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
it('should provide span to callback', async () => {
|
|
263
|
+
let receivedSpan = undefined;
|
|
264
|
+
await withSpan('test.span', {}, async (span) => {
|
|
265
|
+
receivedSpan = span;
|
|
266
|
+
return 'result';
|
|
267
|
+
});
|
|
268
|
+
assert.notStrictEqual(receivedSpan, null);
|
|
269
|
+
assert.ok(receivedSpan !== undefined);
|
|
270
|
+
});
|
|
271
|
+
it('should return function result', async () => {
|
|
272
|
+
const result = await withSpan('test.span', {}, async () => {
|
|
273
|
+
return { data: 'test' };
|
|
274
|
+
});
|
|
275
|
+
assert.deepStrictEqual(result, { data: 'test' });
|
|
276
|
+
});
|
|
277
|
+
it('should propagate errors and record exception', async () => {
|
|
278
|
+
const error = new Error('test error');
|
|
279
|
+
await assert.rejects(async () => {
|
|
280
|
+
await withSpan('test.span', {}, async () => {
|
|
281
|
+
throw error;
|
|
282
|
+
});
|
|
283
|
+
}, (err) => {
|
|
284
|
+
assert.strictEqual(err, error);
|
|
285
|
+
return true;
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
it('should allow setting attributes on span', async () => {
|
|
289
|
+
await withSpan('test.span', { 'initial.attr': 'value' }, async (span) => {
|
|
290
|
+
if (span) {
|
|
291
|
+
span.setAttribute('dynamic.attr', 42);
|
|
292
|
+
}
|
|
293
|
+
return 'ok';
|
|
294
|
+
});
|
|
295
|
+
// No assertion needed - just verify it doesn't throw
|
|
296
|
+
});
|
|
297
|
+
it('should filter undefined attributes', async () => {
|
|
298
|
+
await assert.doesNotReject(async () => {
|
|
299
|
+
await withSpan('test.span', {
|
|
300
|
+
defined: 'value',
|
|
301
|
+
undef: undefined,
|
|
302
|
+
num: 42,
|
|
303
|
+
bool: true,
|
|
304
|
+
}, async () => 'ok');
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
it('should support different span kinds', async () => {
|
|
308
|
+
await assert.doesNotReject(async () => {
|
|
309
|
+
await withSpan('test.internal', {}, async () => 'ok', 'INTERNAL');
|
|
310
|
+
await withSpan('test.server', {}, async () => 'ok', 'SERVER');
|
|
311
|
+
await withSpan('test.client', {}, async () => 'ok', 'CLIENT');
|
|
312
|
+
await withSpan('test.producer', {}, async () => 'ok', 'PRODUCER');
|
|
313
|
+
await withSpan('test.consumer', {}, async () => 'ok', 'CONSUMER');
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
describe('withSpanSync - disabled', () => {
|
|
318
|
+
it('should execute function directly when disabled', () => {
|
|
319
|
+
const result = withSpanSync('test.span', { attr: 'value' }, (span) => {
|
|
320
|
+
assert.strictEqual(span, null);
|
|
321
|
+
return 'result';
|
|
322
|
+
});
|
|
323
|
+
assert.strictEqual(result, 'result');
|
|
324
|
+
});
|
|
325
|
+
it('should propagate errors when disabled', () => {
|
|
326
|
+
const error = new Error('test error');
|
|
327
|
+
assert.throws(() => {
|
|
328
|
+
withSpanSync('test.span', {}, () => {
|
|
329
|
+
throw error;
|
|
330
|
+
});
|
|
331
|
+
}, (err) => {
|
|
332
|
+
assert.strictEqual(err, error);
|
|
333
|
+
return true;
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
describe('withSpanSync - enabled', () => {
|
|
338
|
+
beforeEach(async () => {
|
|
339
|
+
await initializeInstrumentation({
|
|
340
|
+
enabled: true,
|
|
341
|
+
endpoint: 'http://localhost:4318',
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
it('should provide span to callback', () => {
|
|
345
|
+
let receivedSpan = undefined;
|
|
346
|
+
withSpanSync('test.span', {}, (span) => {
|
|
347
|
+
receivedSpan = span;
|
|
348
|
+
return 'result';
|
|
349
|
+
});
|
|
350
|
+
assert.notStrictEqual(receivedSpan, null);
|
|
351
|
+
});
|
|
352
|
+
it('should return function result', () => {
|
|
353
|
+
const result = withSpanSync('test.span', {}, () => {
|
|
354
|
+
return { data: 'test' };
|
|
355
|
+
});
|
|
356
|
+
assert.deepStrictEqual(result, { data: 'test' });
|
|
357
|
+
});
|
|
358
|
+
it('should propagate errors', () => {
|
|
359
|
+
const error = new Error('test error');
|
|
360
|
+
assert.throws(() => {
|
|
361
|
+
withSpanSync('test.span', {}, () => {
|
|
362
|
+
throw error;
|
|
363
|
+
});
|
|
364
|
+
}, (err) => {
|
|
365
|
+
assert.strictEqual(err, error);
|
|
366
|
+
return true;
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
it('should filter undefined attributes', () => {
|
|
370
|
+
assert.doesNotThrow(() => {
|
|
371
|
+
withSpanSync('test.span', {
|
|
372
|
+
defined: 'value',
|
|
373
|
+
undef: undefined,
|
|
374
|
+
}, () => 'ok');
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
describe('graceful degradation', () => {
|
|
379
|
+
it('should not throw when SDK fails to load', async () => {
|
|
380
|
+
// This tests graceful degradation by ensuring errors during init don't crash
|
|
381
|
+
await assert.doesNotReject(async () => {
|
|
382
|
+
// Valid config but SDK might fail for various reasons
|
|
383
|
+
await initializeInstrumentation({
|
|
384
|
+
enabled: true,
|
|
385
|
+
endpoint: 'http://localhost:4318',
|
|
386
|
+
});
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
it('should continue to work after failed initialization', async () => {
|
|
390
|
+
// Even if init fails somehow, withSpan should still work
|
|
391
|
+
resetInstrumentationForTesting();
|
|
392
|
+
const result = await withSpan('test.span', {}, async () => 'ok');
|
|
393
|
+
assert.strictEqual(result, 'ok');
|
|
394
|
+
});
|
|
395
|
+
// M5 fix: Verify re-initialization is possible after proper shutdown
|
|
396
|
+
it('should allow re-initialization after shutdown and reset', async () => {
|
|
397
|
+
// First init
|
|
398
|
+
await initializeInstrumentation({
|
|
399
|
+
enabled: true,
|
|
400
|
+
endpoint: 'http://localhost:4318',
|
|
401
|
+
});
|
|
402
|
+
const tracer1 = getTracer();
|
|
403
|
+
assert.notStrictEqual(tracer1, null);
|
|
404
|
+
// Shutdown and reset
|
|
405
|
+
await shutdownInstrumentation();
|
|
406
|
+
resetInstrumentationForTesting();
|
|
407
|
+
// Second init should work
|
|
408
|
+
await initializeInstrumentation({
|
|
409
|
+
enabled: true,
|
|
410
|
+
endpoint: 'http://localhost:4318',
|
|
411
|
+
});
|
|
412
|
+
const tracer2 = getTracer();
|
|
413
|
+
assert.notStrictEqual(tracer2, null);
|
|
414
|
+
});
|
|
415
|
+
});
|
|
416
|
+
// M4 fix: Add concurrent span tests to verify spans don't interfere
|
|
417
|
+
describe('concurrent spans', () => {
|
|
418
|
+
beforeEach(async () => {
|
|
419
|
+
await initializeInstrumentation({
|
|
420
|
+
enabled: true,
|
|
421
|
+
endpoint: 'http://localhost:4318',
|
|
422
|
+
});
|
|
423
|
+
});
|
|
424
|
+
it('should handle multiple async spans in parallel', async () => {
|
|
425
|
+
const results = await Promise.all([
|
|
426
|
+
withSpan('concurrent.span.1', { id: 1 }, async () => {
|
|
427
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
428
|
+
return 'result1';
|
|
429
|
+
}),
|
|
430
|
+
withSpan('concurrent.span.2', { id: 2 }, async () => {
|
|
431
|
+
await new Promise(resolve => setTimeout(resolve, 5));
|
|
432
|
+
return 'result2';
|
|
433
|
+
}),
|
|
434
|
+
withSpan('concurrent.span.3', { id: 3 }, async () => {
|
|
435
|
+
return 'result3';
|
|
436
|
+
}),
|
|
437
|
+
]);
|
|
438
|
+
assert.deepStrictEqual(results, ['result1', 'result2', 'result3']);
|
|
439
|
+
});
|
|
440
|
+
it('should handle nested spans correctly', async () => {
|
|
441
|
+
const result = await withSpan('outer.span', {}, async (outerSpan) => {
|
|
442
|
+
assert.notStrictEqual(outerSpan, null);
|
|
443
|
+
const innerResult = await withSpan('inner.span', {}, async (innerSpan) => {
|
|
444
|
+
assert.notStrictEqual(innerSpan, null);
|
|
445
|
+
return 'inner';
|
|
446
|
+
});
|
|
447
|
+
return `outer-${innerResult}`;
|
|
448
|
+
});
|
|
449
|
+
assert.strictEqual(result, 'outer-inner');
|
|
450
|
+
});
|
|
451
|
+
it('should isolate errors in concurrent spans', async () => {
|
|
452
|
+
const results = await Promise.allSettled([
|
|
453
|
+
withSpan('success.span', {}, async () => 'success'),
|
|
454
|
+
withSpan('error.span', {}, async () => {
|
|
455
|
+
throw new Error('intentional error');
|
|
456
|
+
}),
|
|
457
|
+
withSpan('success.span.2', {}, async () => 'success2'),
|
|
458
|
+
]);
|
|
459
|
+
assert.strictEqual(results[0].status, 'fulfilled');
|
|
460
|
+
assert.strictEqual(results[0].value, 'success');
|
|
461
|
+
assert.strictEqual(results[1].status, 'rejected');
|
|
462
|
+
assert.strictEqual(results[1].reason.message, 'intentional error');
|
|
463
|
+
assert.strictEqual(results[2].status, 'fulfilled');
|
|
464
|
+
assert.strictEqual(results[2].value, 'success2');
|
|
465
|
+
});
|
|
466
|
+
it('should handle concurrent sync spans', () => {
|
|
467
|
+
// Sync spans don't actually run in parallel, but test they work sequentially
|
|
468
|
+
const result1 = withSpanSync('sync.span.1', {}, () => 'result1');
|
|
469
|
+
const result2 = withSpanSync('sync.span.2', {}, () => 'result2');
|
|
470
|
+
assert.strictEqual(result1, 'result1');
|
|
471
|
+
assert.strictEqual(result2, 'result2');
|
|
472
|
+
});
|
|
473
|
+
it('should handle deeply nested spans', async () => {
|
|
474
|
+
const result = await withSpan('level1', {}, async () => {
|
|
475
|
+
return withSpan('level2', {}, async () => {
|
|
476
|
+
return withSpan('level3', {}, async () => {
|
|
477
|
+
return withSpanSync('level4.sync', {}, () => {
|
|
478
|
+
return 'deepest';
|
|
479
|
+
});
|
|
480
|
+
});
|
|
481
|
+
});
|
|
482
|
+
});
|
|
483
|
+
assert.strictEqual(result, 'deepest');
|
|
484
|
+
});
|
|
485
|
+
it('should handle parallel spans with nested child spans', async () => {
|
|
486
|
+
const results = await Promise.all([
|
|
487
|
+
withSpan('parent.1', {}, async () => {
|
|
488
|
+
const child = await withSpan('child.1', {}, async () => 'child1-result');
|
|
489
|
+
return `parent1-${child}`;
|
|
490
|
+
}),
|
|
491
|
+
withSpan('parent.2', {}, async () => {
|
|
492
|
+
const child = await withSpan('child.2', {}, async () => 'child2-result');
|
|
493
|
+
return `parent2-${child}`;
|
|
494
|
+
}),
|
|
495
|
+
]);
|
|
496
|
+
assert.deepStrictEqual(results, ['parent1-child1-result', 'parent2-child2-result']);
|
|
497
|
+
});
|
|
498
|
+
});
|
|
499
|
+
// TQ2 fix: Error injection tests for various failure modes
|
|
500
|
+
describe('error injection', () => {
|
|
501
|
+
beforeEach(async () => {
|
|
502
|
+
await initializeInstrumentation({
|
|
503
|
+
enabled: true,
|
|
504
|
+
endpoint: 'http://localhost:4318',
|
|
505
|
+
});
|
|
506
|
+
});
|
|
507
|
+
it('should handle TypeError correctly', async () => {
|
|
508
|
+
const typeError = new TypeError('null is not an object');
|
|
509
|
+
await assert.rejects(async () => {
|
|
510
|
+
await withSpan('error.type', {}, async () => {
|
|
511
|
+
throw typeError;
|
|
512
|
+
});
|
|
513
|
+
}, (err) => {
|
|
514
|
+
assert.strictEqual(err, typeError);
|
|
515
|
+
assert.strictEqual(err.constructor.name, 'TypeError');
|
|
516
|
+
return true;
|
|
517
|
+
});
|
|
518
|
+
});
|
|
519
|
+
it('should handle RangeError correctly', async () => {
|
|
520
|
+
const rangeError = new RangeError('value out of range');
|
|
521
|
+
await assert.rejects(async () => {
|
|
522
|
+
await withSpan('error.range', {}, async () => {
|
|
523
|
+
throw rangeError;
|
|
524
|
+
});
|
|
525
|
+
}, (err) => {
|
|
526
|
+
assert.strictEqual(err, rangeError);
|
|
527
|
+
assert.strictEqual(err.constructor.name, 'RangeError');
|
|
528
|
+
return true;
|
|
529
|
+
});
|
|
530
|
+
});
|
|
531
|
+
it('should handle custom error classes', async () => {
|
|
532
|
+
class CustomAppError extends Error {
|
|
533
|
+
code;
|
|
534
|
+
constructor(message, code) {
|
|
535
|
+
super(message);
|
|
536
|
+
this.code = code;
|
|
537
|
+
this.name = 'CustomAppError';
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
const customError = new CustomAppError('app failure', 'E001');
|
|
541
|
+
await assert.rejects(async () => {
|
|
542
|
+
await withSpan('error.custom', {}, async () => {
|
|
543
|
+
throw customError;
|
|
544
|
+
});
|
|
545
|
+
}, (err) => {
|
|
546
|
+
assert.strictEqual(err, customError);
|
|
547
|
+
assert.strictEqual(err.code, 'E001');
|
|
548
|
+
return true;
|
|
549
|
+
});
|
|
550
|
+
});
|
|
551
|
+
it('should handle non-Error throws (string)', async () => {
|
|
552
|
+
await assert.rejects(async () => {
|
|
553
|
+
await withSpan('error.string', {}, async () => {
|
|
554
|
+
throw 'string error';
|
|
555
|
+
});
|
|
556
|
+
}, (err) => {
|
|
557
|
+
assert.strictEqual(err, 'string error');
|
|
558
|
+
return true;
|
|
559
|
+
});
|
|
560
|
+
});
|
|
561
|
+
it('should handle non-Error throws (object)', async () => {
|
|
562
|
+
const errorObj = { code: 'FAIL', message: 'object error' };
|
|
563
|
+
await assert.rejects(async () => {
|
|
564
|
+
await withSpan('error.object', {}, async () => {
|
|
565
|
+
throw errorObj;
|
|
566
|
+
});
|
|
567
|
+
}, (err) => {
|
|
568
|
+
assert.deepStrictEqual(err, errorObj);
|
|
569
|
+
return true;
|
|
570
|
+
});
|
|
571
|
+
});
|
|
572
|
+
it('should handle sync TypeError correctly', () => {
|
|
573
|
+
const typeError = new TypeError('undefined is not a function');
|
|
574
|
+
assert.throws(() => {
|
|
575
|
+
withSpanSync('error.sync.type', {}, () => {
|
|
576
|
+
throw typeError;
|
|
577
|
+
});
|
|
578
|
+
}, (err) => {
|
|
579
|
+
assert.strictEqual(err, typeError);
|
|
580
|
+
return true;
|
|
581
|
+
});
|
|
582
|
+
});
|
|
583
|
+
it('should handle sync non-Error throws', () => {
|
|
584
|
+
assert.throws(() => {
|
|
585
|
+
withSpanSync('error.sync.number', {}, () => {
|
|
586
|
+
throw 42;
|
|
587
|
+
});
|
|
588
|
+
}, (err) => {
|
|
589
|
+
assert.strictEqual(err, 42);
|
|
590
|
+
return true;
|
|
591
|
+
});
|
|
592
|
+
});
|
|
593
|
+
it('should handle error in deeply nested spans', async () => {
|
|
594
|
+
const deepError = new Error('deep failure');
|
|
595
|
+
await assert.rejects(async () => {
|
|
596
|
+
await withSpan('level1', {}, async () => {
|
|
597
|
+
return withSpan('level2', {}, async () => {
|
|
598
|
+
return withSpan('level3', {}, async () => {
|
|
599
|
+
throw deepError;
|
|
600
|
+
});
|
|
601
|
+
});
|
|
602
|
+
});
|
|
603
|
+
}, (err) => {
|
|
604
|
+
assert.strictEqual(err, deepError);
|
|
605
|
+
return true;
|
|
606
|
+
});
|
|
607
|
+
});
|
|
608
|
+
it('should isolate errors in sibling spans', async () => {
|
|
609
|
+
const results = await Promise.allSettled([
|
|
610
|
+
withSpan('sibling.1', {}, async () => {
|
|
611
|
+
throw new Error('sibling 1 error');
|
|
612
|
+
}),
|
|
613
|
+
withSpan('sibling.2', {}, async () => 'sibling 2 ok'),
|
|
614
|
+
]);
|
|
615
|
+
assert.strictEqual(results[0].status, 'rejected');
|
|
616
|
+
assert.strictEqual(results[1].status, 'fulfilled');
|
|
617
|
+
assert.strictEqual(results[1].value, 'sibling 2 ok');
|
|
618
|
+
});
|
|
619
|
+
});
|
|
620
|
+
// P2 fix: Test deferred attribute allocation
|
|
621
|
+
describe('deferred attributes', () => {
|
|
622
|
+
it('should not call attribute function when disabled', async () => {
|
|
623
|
+
resetInstrumentationForTesting();
|
|
624
|
+
let attrFnCalled = false;
|
|
625
|
+
const result = await withSpan('test.deferred', () => {
|
|
626
|
+
attrFnCalled = true;
|
|
627
|
+
return { attr: 'value' };
|
|
628
|
+
}, async () => 'result');
|
|
629
|
+
assert.strictEqual(result, 'result');
|
|
630
|
+
assert.strictEqual(attrFnCalled, false, 'Attribute function should not be called when disabled');
|
|
631
|
+
});
|
|
632
|
+
it('should call attribute function when enabled', async () => {
|
|
633
|
+
await initializeInstrumentation({
|
|
634
|
+
enabled: true,
|
|
635
|
+
endpoint: 'http://localhost:4318',
|
|
636
|
+
});
|
|
637
|
+
let attrFnCalled = false;
|
|
638
|
+
const result = await withSpan('test.deferred.enabled', () => {
|
|
639
|
+
attrFnCalled = true;
|
|
640
|
+
return { attr: 'value' };
|
|
641
|
+
}, async () => 'result');
|
|
642
|
+
assert.strictEqual(result, 'result');
|
|
643
|
+
assert.strictEqual(attrFnCalled, true, 'Attribute function should be called when enabled');
|
|
644
|
+
});
|
|
645
|
+
it('should support deferred attributes in withSpanSync when disabled', () => {
|
|
646
|
+
resetInstrumentationForTesting();
|
|
647
|
+
let attrFnCalled = false;
|
|
648
|
+
const result = withSpanSync('test.sync.deferred', () => {
|
|
649
|
+
attrFnCalled = true;
|
|
650
|
+
return { attr: 'value' };
|
|
651
|
+
}, () => 'sync-result');
|
|
652
|
+
assert.strictEqual(result, 'sync-result');
|
|
653
|
+
assert.strictEqual(attrFnCalled, false, 'Attribute function should not be called when disabled');
|
|
654
|
+
});
|
|
655
|
+
it('should support deferred attributes in withSpanSync when enabled', async () => {
|
|
656
|
+
await initializeInstrumentation({
|
|
657
|
+
enabled: true,
|
|
658
|
+
endpoint: 'http://localhost:4318',
|
|
659
|
+
});
|
|
660
|
+
let attrFnCalled = false;
|
|
661
|
+
const result = withSpanSync('test.sync.deferred.enabled', () => {
|
|
662
|
+
attrFnCalled = true;
|
|
663
|
+
return { attr: 'value' };
|
|
664
|
+
}, () => 'sync-result');
|
|
665
|
+
assert.strictEqual(result, 'sync-result');
|
|
666
|
+
assert.strictEqual(attrFnCalled, true, 'Attribute function should be called when enabled');
|
|
667
|
+
});
|
|
668
|
+
});
|
|
669
|
+
// M1-B4 fix: Tests for initialization status tracking
|
|
670
|
+
describe('initialization status (M1-B4)', () => {
|
|
671
|
+
it('should return pending status before initialization', () => {
|
|
672
|
+
const status = getInitializationStatus();
|
|
673
|
+
assert.strictEqual(status.status, 'pending');
|
|
674
|
+
assert.strictEqual(status.error, undefined);
|
|
675
|
+
assert.strictEqual(status.retryCount, undefined);
|
|
676
|
+
});
|
|
677
|
+
it('should return pending from waitForInitialization before init called', async () => {
|
|
678
|
+
const result = await waitForInitialization();
|
|
679
|
+
assert.strictEqual(result.status, 'pending');
|
|
680
|
+
});
|
|
681
|
+
it('should return disabled status when OTEL_ENABLED is not set', async () => {
|
|
682
|
+
delete process.env.OTEL_ENABLED;
|
|
683
|
+
await initializeInstrumentation();
|
|
684
|
+
const syncStatus = getInitializationStatus();
|
|
685
|
+
assert.strictEqual(syncStatus.status, 'disabled');
|
|
686
|
+
const asyncStatus = await waitForInitialization();
|
|
687
|
+
assert.strictEqual(asyncStatus.status, 'disabled');
|
|
688
|
+
});
|
|
689
|
+
it('should return disabled status when no endpoint configured', async () => {
|
|
690
|
+
process.env.OTEL_ENABLED = 'true';
|
|
691
|
+
delete process.env.OTEL_EXPORTER_OTLP_ENDPOINT;
|
|
692
|
+
await initializeInstrumentation();
|
|
693
|
+
const status = getInitializationStatus();
|
|
694
|
+
assert.strictEqual(status.status, 'disabled');
|
|
695
|
+
});
|
|
696
|
+
it('should return ready status when successfully initialized', async () => {
|
|
697
|
+
await initializeInstrumentation({
|
|
698
|
+
enabled: true,
|
|
699
|
+
endpoint: 'http://localhost:4318',
|
|
700
|
+
});
|
|
701
|
+
const syncStatus = getInitializationStatus();
|
|
702
|
+
assert.strictEqual(syncStatus.status, 'ready');
|
|
703
|
+
const asyncStatus = await waitForInitialization();
|
|
704
|
+
assert.strictEqual(asyncStatus.status, 'ready');
|
|
705
|
+
});
|
|
706
|
+
it('should reset status on shutdown', async () => {
|
|
707
|
+
await initializeInstrumentation({
|
|
708
|
+
enabled: true,
|
|
709
|
+
endpoint: 'http://localhost:4318',
|
|
710
|
+
});
|
|
711
|
+
assert.strictEqual(getInitializationStatus().status, 'ready');
|
|
712
|
+
await shutdownInstrumentation();
|
|
713
|
+
assert.strictEqual(getInitializationStatus().status, 'pending');
|
|
714
|
+
});
|
|
715
|
+
it('should reset status on resetInstrumentationForTesting', async () => {
|
|
716
|
+
await initializeInstrumentation({
|
|
717
|
+
enabled: true,
|
|
718
|
+
endpoint: 'http://localhost:4318',
|
|
719
|
+
});
|
|
720
|
+
assert.strictEqual(getInitializationStatus().status, 'ready');
|
|
721
|
+
resetInstrumentationForTesting();
|
|
722
|
+
assert.strictEqual(getInitializationStatus().status, 'pending');
|
|
723
|
+
});
|
|
724
|
+
it('waitForInitialization should return same result on multiple calls', async () => {
|
|
725
|
+
await initializeInstrumentation({
|
|
726
|
+
enabled: true,
|
|
727
|
+
endpoint: 'http://localhost:4318',
|
|
728
|
+
});
|
|
729
|
+
const result1 = await waitForInitialization();
|
|
730
|
+
const result2 = await waitForInitialization();
|
|
731
|
+
assert.strictEqual(result1.status, 'ready');
|
|
732
|
+
assert.strictEqual(result2.status, 'ready');
|
|
733
|
+
});
|
|
734
|
+
it('should include retryCount when initialization succeeds', async () => {
|
|
735
|
+
await initializeInstrumentation({
|
|
736
|
+
enabled: true,
|
|
737
|
+
endpoint: 'http://localhost:4318',
|
|
738
|
+
});
|
|
739
|
+
const result = await waitForInitialization();
|
|
740
|
+
assert.strictEqual(result.status, 'ready');
|
|
741
|
+
// retryCount should be 0 on first successful init (may be undefined or 0)
|
|
742
|
+
assert.ok(result.retryCount === undefined || result.retryCount === 0);
|
|
743
|
+
});
|
|
744
|
+
});
|
|
745
|
+
// A1 fix: Test InstrumentationManager for isolated instances
|
|
746
|
+
describe('InstrumentationManager (A1 fix)', () => {
|
|
747
|
+
it('should export InstrumentationManager class', () => {
|
|
748
|
+
assert.ok(InstrumentationManager);
|
|
749
|
+
assert.strictEqual(typeof InstrumentationManager, 'function');
|
|
750
|
+
});
|
|
751
|
+
it('should export getDefaultManager function', () => {
|
|
752
|
+
assert.ok(getDefaultManager);
|
|
753
|
+
assert.strictEqual(typeof getDefaultManager, 'function');
|
|
754
|
+
});
|
|
755
|
+
it('should create isolated instances', () => {
|
|
756
|
+
const manager1 = new InstrumentationManager();
|
|
757
|
+
const manager2 = new InstrumentationManager();
|
|
758
|
+
assert.notStrictEqual(manager1, manager2);
|
|
759
|
+
});
|
|
760
|
+
it('should allow independent initialization of isolated instances', async () => {
|
|
761
|
+
const manager1 = new InstrumentationManager();
|
|
762
|
+
const manager2 = new InstrumentationManager();
|
|
763
|
+
// Initialize manager1 with enabled
|
|
764
|
+
await manager1.initialize({
|
|
765
|
+
enabled: true,
|
|
766
|
+
endpoint: 'http://localhost:4318',
|
|
767
|
+
});
|
|
768
|
+
// manager2 should still be uninitialized
|
|
769
|
+
assert.notStrictEqual(manager1.getTracer(), null);
|
|
770
|
+
assert.strictEqual(manager2.getTracer(), null);
|
|
771
|
+
// Clean up
|
|
772
|
+
await manager1.shutdown();
|
|
773
|
+
});
|
|
774
|
+
it('should allow isolated instances to be reset independently', async () => {
|
|
775
|
+
const manager1 = new InstrumentationManager();
|
|
776
|
+
const manager2 = new InstrumentationManager();
|
|
777
|
+
await manager1.initialize({ enabled: true, endpoint: 'http://localhost:4318' });
|
|
778
|
+
await manager2.initialize({ enabled: true, endpoint: 'http://localhost:4318' });
|
|
779
|
+
assert.notStrictEqual(manager1.getTracer(), null);
|
|
780
|
+
assert.notStrictEqual(manager2.getTracer(), null);
|
|
781
|
+
// Reset only manager1
|
|
782
|
+
manager1.reset();
|
|
783
|
+
assert.strictEqual(manager1.getTracer(), null);
|
|
784
|
+
assert.notStrictEqual(manager2.getTracer(), null);
|
|
785
|
+
// Clean up
|
|
786
|
+
await manager2.shutdown();
|
|
787
|
+
});
|
|
788
|
+
it('should have isEnabled method on instance', () => {
|
|
789
|
+
const manager = new InstrumentationManager();
|
|
790
|
+
assert.strictEqual(manager.isEnabled({ enabled: false }), false);
|
|
791
|
+
assert.strictEqual(manager.isEnabled({ enabled: true }), true);
|
|
792
|
+
});
|
|
793
|
+
it('should have withSpan method on instance', async () => {
|
|
794
|
+
const manager = new InstrumentationManager();
|
|
795
|
+
const result = await manager.withSpan('test', {}, async () => 'ok');
|
|
796
|
+
assert.strictEqual(result, 'ok');
|
|
797
|
+
});
|
|
798
|
+
it('should have withSpanSync method on instance', () => {
|
|
799
|
+
const manager = new InstrumentationManager();
|
|
800
|
+
const result = manager.withSpanSync('test', {}, () => 'ok');
|
|
801
|
+
assert.strictEqual(result, 'ok');
|
|
802
|
+
});
|
|
803
|
+
it('getDefaultManager should return the singleton', () => {
|
|
804
|
+
const defaultManager = getDefaultManager();
|
|
805
|
+
assert.ok(defaultManager instanceof InstrumentationManager);
|
|
806
|
+
// Module-level functions should delegate to the same instance
|
|
807
|
+
assert.strictEqual(defaultManager.getTracer(), getTracer());
|
|
808
|
+
});
|
|
809
|
+
it('should not interfere with module-level functions', async () => {
|
|
810
|
+
// Create an isolated manager
|
|
811
|
+
const isolated = new InstrumentationManager();
|
|
812
|
+
await isolated.initialize({ enabled: true, endpoint: 'http://localhost:4318' });
|
|
813
|
+
// Module-level tracer should still be null (controlled by resetInstrumentationForTesting in afterEach)
|
|
814
|
+
assert.strictEqual(getTracer(), null);
|
|
815
|
+
assert.notStrictEqual(isolated.getTracer(), null);
|
|
816
|
+
// Clean up
|
|
817
|
+
await isolated.shutdown();
|
|
818
|
+
});
|
|
819
|
+
});
|
|
820
|
+
});
|
|
821
|
+
//# sourceMappingURL=instrumentation.test.js.map
|