@geekmidas/telescope 1.1.1 → 10.0.0-alpha.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 +14 -4
- package/dist/{hono-Bz9PQQzj.mjs → hono-Bw-ey6CT.mjs} +2 -2
- package/dist/{hono-Bz9PQQzj.mjs.map → hono-Bw-ey6CT.mjs.map} +1 -1
- package/dist/{hono-Z_JIICmt.cjs → hono-CPHX7FAm.cjs} +2 -2
- package/dist/{hono-Z_JIICmt.cjs.map → hono-CPHX7FAm.cjs.map} +1 -1
- package/dist/{http-Crsriv32.mjs → http-BIvr0YdI.mjs} +1 -1
- package/dist/{http-Crsriv32.mjs.map → http-BIvr0YdI.mjs.map} +1 -1
- package/dist/{http-JycFRfSe.cjs → http-BsqmhurZ.cjs} +1 -1
- package/dist/{http-JycFRfSe.cjs.map → http-BsqmhurZ.cjs.map} +1 -1
- package/dist/{http-D5kn3_Mt.d.mts → http-DULM1u2L.d.cts} +3 -3
- package/dist/{http-D5kn3_Mt.d.mts.map → http-DULM1u2L.d.cts.map} +1 -1
- package/dist/{http-Dvca6fC3.d.cts → http-GB-Nlvwc.d.mts} +3 -3
- package/dist/{http-Dvca6fC3.d.cts.map → http-GB-Nlvwc.d.mts.map} +1 -1
- package/dist/instrumentation/hono.cjs +2 -2
- package/dist/instrumentation/hono.mjs +2 -2
- package/dist/instrumentation/http.cjs +1 -1
- package/dist/instrumentation/http.d.cts +1 -1
- package/dist/instrumentation/http.d.mts +1 -1
- package/dist/instrumentation/http.mjs +1 -1
- package/dist/instrumentation/index.cjs +5 -5
- package/dist/instrumentation/index.d.cts +3 -3
- package/dist/instrumentation/index.d.mts +3 -3
- package/dist/instrumentation/index.mjs +5 -5
- package/dist/instrumentation/middleware.cjs +2 -2
- package/dist/instrumentation/middleware.d.cts +1 -1
- package/dist/instrumentation/middleware.d.mts +1 -1
- package/dist/instrumentation/middleware.mjs +2 -2
- package/dist/instrumentation/otel.cjs +2 -2
- package/dist/instrumentation/otel.d.cts +1 -1
- package/dist/instrumentation/otel.d.mts +1 -1
- package/dist/instrumentation/otel.mjs +2 -2
- package/dist/instrumentation/setup.cjs +1 -1
- package/dist/instrumentation/setup.mjs +1 -1
- package/dist/logger/console.d.cts.map +1 -1
- package/dist/{middleware-D3v5SnSs.cjs → middleware-Bcq6XXGG.cjs} +3 -3
- package/dist/middleware-Bcq6XXGG.cjs.map +1 -0
- package/dist/{middleware-Dy1ctr2N.mjs → middleware-D1XEHvyy.mjs} +3 -3
- package/dist/middleware-D1XEHvyy.mjs.map +1 -0
- package/dist/{middleware-CA84nsNN.d.cts → middleware-PeyS-9Ny.d.cts} +2 -2
- package/dist/{middleware-CA84nsNN.d.cts.map → middleware-PeyS-9Ny.d.cts.map} +1 -1
- package/dist/{middleware-DAceVpfw.d.mts → middleware-oHvo_ZgU.d.mts} +2 -2
- package/dist/{middleware-DAceVpfw.d.mts.map → middleware-oHvo_ZgU.d.mts.map} +1 -1
- package/dist/{otel-DPKNyJ0t.mjs → otel-B9bNoeZ2.mjs} +4 -4
- package/dist/otel-B9bNoeZ2.mjs.map +1 -0
- package/dist/{otel-1bJPb2Z7.d.mts → otel-Bk89b1lX.d.cts} +3 -3
- package/dist/{otel-1bJPb2Z7.d.mts.map → otel-Bk89b1lX.d.cts.map} +1 -1
- package/dist/{otel-DuiBeBPU.cjs → otel-BzjOtVxZ.cjs} +4 -4
- package/dist/otel-BzjOtVxZ.cjs.map +1 -0
- package/dist/{otel-SBVoVEHr.d.cts → otel-lPMsM26e.d.mts} +3 -3
- package/dist/{otel-SBVoVEHr.d.cts.map → otel-lPMsM26e.d.mts.map} +1 -1
- package/dist/otlp/hono.cjs +3 -3
- package/dist/otlp/hono.mjs +3 -3
- package/dist/otlp/index.cjs +3 -3
- package/dist/otlp/index.mjs +3 -3
- package/dist/otlp/receiver.cjs +3 -3
- package/dist/otlp/receiver.mjs +3 -3
- package/dist/otlp/transformer.cjs +2 -2
- package/dist/otlp/transformer.mjs +2 -2
- package/dist/otlp/types.cjs +1 -1
- package/dist/otlp/types.mjs +1 -1
- package/dist/{receiver-q4f7eirs.mjs → receiver-D46dfkYI.mjs} +2 -2
- package/dist/{receiver-q4f7eirs.mjs.map → receiver-D46dfkYI.mjs.map} +1 -1
- package/dist/{receiver-BW_4go_X.cjs → receiver-slCoAFat.cjs} +2 -2
- package/dist/{receiver-BW_4go_X.cjs.map → receiver-slCoAFat.cjs.map} +1 -1
- package/dist/{setup-BhCp2LUp.mjs → setup-DC9JMEYZ.mjs} +1 -1
- package/dist/{setup-BhCp2LUp.mjs.map → setup-DC9JMEYZ.mjs.map} +1 -1
- package/dist/{setup-_uT4L_x5.cjs → setup-DufxQpi9.cjs} +1 -1
- package/dist/{setup-_uT4L_x5.cjs.map → setup-DufxQpi9.cjs.map} +1 -1
- package/dist/storage/kysely.cjs +48 -38
- package/dist/storage/kysely.cjs.map +1 -1
- package/dist/storage/kysely.d.cts +29 -9
- package/dist/storage/kysely.d.cts.map +1 -1
- package/dist/storage/kysely.d.mts +29 -9
- package/dist/storage/kysely.d.mts.map +1 -1
- package/dist/storage/kysely.mjs +48 -38
- package/dist/storage/kysely.mjs.map +1 -1
- package/dist/tracing-BX8uY2nR.d.cts.map +1 -1
- package/dist/tracing-PnECEbEu.d.mts.map +1 -1
- package/dist/{transformer-CFrJQkUg.mjs → transformer-CFQ4WFk6.mjs} +2 -2
- package/dist/{transformer-CFrJQkUg.mjs.map → transformer-CFQ4WFk6.mjs.map} +1 -1
- package/dist/{transformer-DvDYyFRQ.cjs → transformer-Csl6fbgv.cjs} +2 -2
- package/dist/{transformer-DvDYyFRQ.cjs.map → transformer-Csl6fbgv.cjs.map} +1 -1
- package/dist/types-B1EIa_cQ.d.mts.map +1 -1
- package/dist/{types-BBeTDpHr.cjs → types-BMiHgsi4.cjs} +1 -1
- package/dist/{types-BBeTDpHr.cjs.map → types-BMiHgsi4.cjs.map} +1 -1
- package/dist/{types-CXYLhG_G.mjs → types-Cj59nqy7.mjs} +1 -1
- package/dist/{types-CXYLhG_G.mjs.map → types-Cj59nqy7.mjs.map} +1 -1
- package/dist/types-l7Rlb5Mg.d.cts.map +1 -1
- package/package.json +6 -3
- package/CHANGELOG.md +0 -51
- package/dist/middleware-D3v5SnSs.cjs.map +0 -1
- package/dist/middleware-Dy1ctr2N.mjs.map +0 -1
- package/dist/otel-DPKNyJ0t.mjs.map +0 -1
- package/dist/otel-DuiBeBPU.cjs.map +0 -1
- package/src/Telescope.ts +0 -561
- package/src/__tests__/Telescope.spec.ts +0 -438
- package/src/__tests__/pino-transport.spec.ts +0 -115
- package/src/__tests__/redact.spec.ts +0 -336
- package/src/adapters/__tests__/lambda.spec.ts +0 -318
- package/src/adapters/__tests__/middy.spec.ts +0 -741
- package/src/adapters/index.ts +0 -22
- package/src/adapters/lambda.ts +0 -432
- package/src/adapters/middy.ts +0 -402
- package/src/adapters/types.ts +0 -181
- package/src/core.ts +0 -51
- package/src/index.ts +0 -34
- package/src/instrumentation/__tests__/core.spec.ts +0 -248
- package/src/instrumentation/__tests__/hono.spec.ts +0 -348
- package/src/instrumentation/__tests__/middleware.spec.ts +0 -528
- package/src/instrumentation/__tests__/otel.spec.ts +0 -291
- package/src/instrumentation/__tests__/tracing.spec.ts +0 -208
- package/src/instrumentation/core.ts +0 -228
- package/src/instrumentation/hono.ts +0 -270
- package/src/instrumentation/http.ts +0 -306
- package/src/instrumentation/index.ts +0 -90
- package/src/instrumentation/middleware.ts +0 -297
- package/src/instrumentation/otel.ts +0 -271
- package/src/instrumentation/setup.ts +0 -246
- package/src/instrumentation/tracing.ts +0 -145
- package/src/logger/__tests__/console.spec.ts +0 -366
- package/src/logger/__tests__/pino.spec.ts +0 -217
- package/src/logger/console.ts +0 -230
- package/src/logger/pino.ts +0 -201
- package/src/metrics/MetricsAggregator.ts +0 -555
- package/src/metrics/__tests__/MetricsAggregator.spec.ts +0 -298
- package/src/metrics/index.ts +0 -2
- package/src/metrics/storage/InMemoryMetricsStorage.ts +0 -380
- package/src/metrics/storage/MetricsStorage.ts +0 -74
- package/src/metrics/storage/index.ts +0 -3
- package/src/metrics/storage/types.ts +0 -243
- package/src/otlp/__tests__/receiver.spec.ts +0 -367
- package/src/otlp/__tests__/transformer.spec.ts +0 -455
- package/src/otlp/hono.ts +0 -120
- package/src/otlp/index.ts +0 -22
- package/src/otlp/receiver.ts +0 -162
- package/src/otlp/transformer.ts +0 -367
- package/src/otlp/types.ts +0 -364
- package/src/redact.ts +0 -116
- package/src/server/__tests__/hono.spec.ts +0 -814
- package/src/server/hono.ts +0 -442
- package/src/storage/__tests__/kysely.spec.ts +0 -715
- package/src/storage/__tests__/memory.spec.ts +0 -694
- package/src/storage/kysely.ts +0 -599
- package/src/storage/memory.ts +0 -204
- package/src/types.ts +0 -447
- package/src/ui-assets.ts +0 -40
- package/tsconfig.json +0 -9
- package/ui/CHANGELOG.md +0 -7
- package/ui/index.html +0 -12
- package/ui/node_modules/.bin/tsc +0 -21
- package/ui/node_modules/.bin/tsserver +0 -21
- package/ui/node_modules/.bin/vite +0 -21
- package/ui/package.json +0 -24
- package/ui/src/App.tsx +0 -595
- package/ui/src/api.ts +0 -88
- package/ui/src/components/ExceptionDetail.tsx +0 -100
- package/ui/src/components/FilterBar.tsx +0 -162
- package/ui/src/components/LogDetail.tsx +0 -91
- package/ui/src/components/RequestDetail.tsx +0 -143
- package/ui/src/main.tsx +0 -10
- package/ui/src/styles.css +0 -11
- package/ui/src/types.ts +0 -63
- package/ui/src/vite-env.d.ts +0 -1
- package/ui/src/vite-plugin-gkm-config.ts +0 -54
- package/ui/tsconfig.json +0 -20
- package/ui/tsconfig.tsbuildinfo +0 -1
- package/ui/vite.config.ts +0 -13
|
@@ -1,248 +0,0 @@
|
|
|
1
|
-
import type { SpanExporter } from '@opentelemetry/sdk-trace-base';
|
|
2
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
-
import {
|
|
4
|
-
createSpanProcessor,
|
|
5
|
-
flushTelemetry,
|
|
6
|
-
getRecommendedStrategy,
|
|
7
|
-
isTelemetryInitialized,
|
|
8
|
-
NoopSpanProcessor,
|
|
9
|
-
setGlobalLogProcessor,
|
|
10
|
-
setGlobalSpanProcessor,
|
|
11
|
-
shutdownTelemetry,
|
|
12
|
-
withTelemetryFlush,
|
|
13
|
-
} from '../core';
|
|
14
|
-
|
|
15
|
-
describe('core telemetry utilities', () => {
|
|
16
|
-
beforeEach(async () => {
|
|
17
|
-
// Reset state before each test
|
|
18
|
-
await shutdownTelemetry();
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
afterEach(async () => {
|
|
22
|
-
await shutdownTelemetry();
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
describe('createSpanProcessor', () => {
|
|
26
|
-
const mockExporter: SpanExporter = {
|
|
27
|
-
export: vi.fn(),
|
|
28
|
-
shutdown: vi.fn().mockResolvedValue(undefined),
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
it('should create a simple span processor when strategy is simple', () => {
|
|
32
|
-
const processor = createSpanProcessor(mockExporter, {
|
|
33
|
-
strategy: 'simple',
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
expect(processor).toBeDefined();
|
|
37
|
-
expect(processor.forceFlush).toBeDefined();
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('should create a batch span processor when strategy is batch', () => {
|
|
41
|
-
const processor = createSpanProcessor(mockExporter, {
|
|
42
|
-
strategy: 'batch',
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
expect(processor).toBeDefined();
|
|
46
|
-
expect(processor.forceFlush).toBeDefined();
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('should create batch processor with custom options', () => {
|
|
50
|
-
const processor = createSpanProcessor(mockExporter, {
|
|
51
|
-
strategy: 'batch',
|
|
52
|
-
maxQueueSize: 1024,
|
|
53
|
-
scheduledDelayMillis: 1000,
|
|
54
|
-
exportTimeoutMillis: 10000,
|
|
55
|
-
maxExportBatchSize: 256,
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
expect(processor).toBeDefined();
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('should default to batch when strategy is not simple', () => {
|
|
62
|
-
const processor = createSpanProcessor(mockExporter, {
|
|
63
|
-
strategy: 'batch',
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
expect(processor).toBeDefined();
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
describe('getRecommendedStrategy', () => {
|
|
71
|
-
it('should return simple for lambda environment', () => {
|
|
72
|
-
expect(getRecommendedStrategy('lambda')).toBe('simple');
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it('should return simple for edge environment', () => {
|
|
76
|
-
expect(getRecommendedStrategy('edge')).toBe('simple');
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('should return batch for server environment', () => {
|
|
80
|
-
expect(getRecommendedStrategy('server')).toBe('batch');
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('should return batch for custom environment', () => {
|
|
84
|
-
expect(getRecommendedStrategy('custom')).toBe('batch');
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
describe('global state management', () => {
|
|
89
|
-
it('should track initialization state', () => {
|
|
90
|
-
expect(isTelemetryInitialized()).toBe(false);
|
|
91
|
-
|
|
92
|
-
const processor = new NoopSpanProcessor();
|
|
93
|
-
setGlobalSpanProcessor(processor);
|
|
94
|
-
|
|
95
|
-
expect(isTelemetryInitialized()).toBe(true);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it('should set and use global log processor', () => {
|
|
99
|
-
const mockLogProcessor = {
|
|
100
|
-
forceFlush: vi.fn().mockResolvedValue(undefined),
|
|
101
|
-
shutdown: vi.fn().mockResolvedValue(undefined),
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
setGlobalLogProcessor(mockLogProcessor);
|
|
105
|
-
// The log processor is stored internally
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
describe('flushTelemetry', () => {
|
|
110
|
-
it('should flush span processor', async () => {
|
|
111
|
-
const processor = new NoopSpanProcessor();
|
|
112
|
-
const flushSpy = vi.spyOn(processor, 'forceFlush');
|
|
113
|
-
setGlobalSpanProcessor(processor);
|
|
114
|
-
|
|
115
|
-
await flushTelemetry();
|
|
116
|
-
|
|
117
|
-
expect(flushSpy).toHaveBeenCalled();
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it('should flush log processor if available', async () => {
|
|
121
|
-
const mockLogProcessor = {
|
|
122
|
-
forceFlush: vi.fn().mockResolvedValue(undefined),
|
|
123
|
-
};
|
|
124
|
-
setGlobalLogProcessor(mockLogProcessor);
|
|
125
|
-
|
|
126
|
-
await flushTelemetry();
|
|
127
|
-
|
|
128
|
-
expect(mockLogProcessor.forceFlush).toHaveBeenCalled();
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
it('should handle flush timeout gracefully', async () => {
|
|
132
|
-
const slowProcessor = {
|
|
133
|
-
forceFlush: vi
|
|
134
|
-
.fn()
|
|
135
|
-
.mockImplementation(
|
|
136
|
-
() => new Promise((resolve) => setTimeout(resolve, 100000)),
|
|
137
|
-
),
|
|
138
|
-
onStart: vi.fn(),
|
|
139
|
-
onEnd: vi.fn(),
|
|
140
|
-
shutdown: vi.fn().mockResolvedValue(undefined),
|
|
141
|
-
};
|
|
142
|
-
setGlobalSpanProcessor(slowProcessor);
|
|
143
|
-
|
|
144
|
-
// Should not throw, should timeout gracefully
|
|
145
|
-
await flushTelemetry(10); // Very short timeout
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
it('should do nothing if not initialized', async () => {
|
|
149
|
-
// No processor set, should complete without error
|
|
150
|
-
await flushTelemetry();
|
|
151
|
-
});
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
describe('shutdownTelemetry', () => {
|
|
155
|
-
it('should shutdown span processor', async () => {
|
|
156
|
-
const processor = new NoopSpanProcessor();
|
|
157
|
-
const shutdownSpy = vi.spyOn(processor, 'shutdown');
|
|
158
|
-
setGlobalSpanProcessor(processor);
|
|
159
|
-
|
|
160
|
-
await shutdownTelemetry();
|
|
161
|
-
|
|
162
|
-
expect(shutdownSpy).toHaveBeenCalled();
|
|
163
|
-
expect(isTelemetryInitialized()).toBe(false);
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
it('should shutdown log processor if available', async () => {
|
|
167
|
-
const mockLogProcessor = {
|
|
168
|
-
forceFlush: vi.fn().mockResolvedValue(undefined),
|
|
169
|
-
shutdown: vi.fn().mockResolvedValue(undefined),
|
|
170
|
-
};
|
|
171
|
-
setGlobalLogProcessor(mockLogProcessor);
|
|
172
|
-
|
|
173
|
-
await shutdownTelemetry();
|
|
174
|
-
|
|
175
|
-
expect(mockLogProcessor.shutdown).toHaveBeenCalled();
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
it('should handle multiple shutdowns gracefully', async () => {
|
|
179
|
-
const processor = new NoopSpanProcessor();
|
|
180
|
-
setGlobalSpanProcessor(processor);
|
|
181
|
-
|
|
182
|
-
await shutdownTelemetry();
|
|
183
|
-
await shutdownTelemetry(); // Second call should be safe
|
|
184
|
-
});
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
describe('withTelemetryFlush', () => {
|
|
188
|
-
it('should wrap handler and flush after execution', async () => {
|
|
189
|
-
const processor = new NoopSpanProcessor();
|
|
190
|
-
const flushSpy = vi.spyOn(processor, 'forceFlush');
|
|
191
|
-
setGlobalSpanProcessor(processor);
|
|
192
|
-
|
|
193
|
-
const mockHandler = vi.fn().mockResolvedValue({ result: 'success' });
|
|
194
|
-
const wrappedHandler = withTelemetryFlush(mockHandler);
|
|
195
|
-
|
|
196
|
-
const result = await wrappedHandler({ event: 'test' }, {});
|
|
197
|
-
|
|
198
|
-
expect(result).toEqual({ result: 'success' });
|
|
199
|
-
expect(mockHandler).toHaveBeenCalledWith({ event: 'test' }, {});
|
|
200
|
-
expect(flushSpy).toHaveBeenCalled();
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it('should flush even when handler throws', async () => {
|
|
204
|
-
const processor = new NoopSpanProcessor();
|
|
205
|
-
const flushSpy = vi.spyOn(processor, 'forceFlush');
|
|
206
|
-
setGlobalSpanProcessor(processor);
|
|
207
|
-
|
|
208
|
-
const mockHandler = vi.fn().mockRejectedValue(new Error('Handler error'));
|
|
209
|
-
const wrappedHandler = withTelemetryFlush(mockHandler);
|
|
210
|
-
|
|
211
|
-
await expect(wrappedHandler({ event: 'test' }, {})).rejects.toThrow(
|
|
212
|
-
'Handler error',
|
|
213
|
-
);
|
|
214
|
-
expect(flushSpy).toHaveBeenCalled();
|
|
215
|
-
});
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
describe('NoopSpanProcessor', () => {
|
|
219
|
-
it('should implement SpanProcessor interface', () => {
|
|
220
|
-
const processor = new NoopSpanProcessor();
|
|
221
|
-
|
|
222
|
-
expect(processor.forceFlush).toBeDefined();
|
|
223
|
-
expect(processor.onStart).toBeDefined();
|
|
224
|
-
expect(processor.onEnd).toBeDefined();
|
|
225
|
-
expect(processor.shutdown).toBeDefined();
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
it('should resolve forceFlush immediately', async () => {
|
|
229
|
-
const processor = new NoopSpanProcessor();
|
|
230
|
-
await processor.forceFlush();
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
it('should resolve shutdown immediately', async () => {
|
|
234
|
-
const processor = new NoopSpanProcessor();
|
|
235
|
-
await processor.shutdown();
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
it('should do nothing on onStart', () => {
|
|
239
|
-
const processor = new NoopSpanProcessor();
|
|
240
|
-
processor.onStart({} as any);
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
it('should do nothing on onEnd', () => {
|
|
244
|
-
const processor = new NoopSpanProcessor();
|
|
245
|
-
processor.onEnd({} as any);
|
|
246
|
-
});
|
|
247
|
-
});
|
|
248
|
-
});
|
|
@@ -1,348 +0,0 @@
|
|
|
1
|
-
import { trace } from '@opentelemetry/api';
|
|
2
|
-
import {
|
|
3
|
-
BasicTracerProvider,
|
|
4
|
-
InMemorySpanExporter,
|
|
5
|
-
SimpleSpanProcessor,
|
|
6
|
-
} from '@opentelemetry/sdk-trace-base';
|
|
7
|
-
import { Hono } from 'hono';
|
|
8
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
9
|
-
import {
|
|
10
|
-
getSpanFromContext,
|
|
11
|
-
getTraceContextFromHono,
|
|
12
|
-
honoTelemetryMiddleware,
|
|
13
|
-
withHonoSpanContext,
|
|
14
|
-
} from '../hono';
|
|
15
|
-
|
|
16
|
-
describe('honoTelemetryMiddleware', () => {
|
|
17
|
-
let app: Hono;
|
|
18
|
-
let exporter: InMemorySpanExporter;
|
|
19
|
-
let provider: BasicTracerProvider;
|
|
20
|
-
|
|
21
|
-
beforeEach(() => {
|
|
22
|
-
// Set up real OpenTelemetry with in-memory exporter
|
|
23
|
-
exporter = new InMemorySpanExporter();
|
|
24
|
-
provider = new BasicTracerProvider();
|
|
25
|
-
provider.addSpanProcessor(new SimpleSpanProcessor(exporter));
|
|
26
|
-
provider.register();
|
|
27
|
-
|
|
28
|
-
app = new Hono();
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
afterEach(async () => {
|
|
32
|
-
exporter.reset();
|
|
33
|
-
await provider.shutdown();
|
|
34
|
-
trace.disable();
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
describe('span creation', () => {
|
|
38
|
-
it('should create span for GET requests', async () => {
|
|
39
|
-
app.use('*', honoTelemetryMiddleware());
|
|
40
|
-
app.get('/api/users', (c) => {
|
|
41
|
-
const span = getSpanFromContext(c);
|
|
42
|
-
expect(span).toBeDefined();
|
|
43
|
-
return c.json({ users: [] });
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
const response = await app.request('/api/users');
|
|
47
|
-
|
|
48
|
-
expect(response.status).toBe(200);
|
|
49
|
-
const spans = exporter.getFinishedSpans();
|
|
50
|
-
expect(spans).toHaveLength(1);
|
|
51
|
-
expect(spans[0].name).toBe('GET /api/users');
|
|
52
|
-
expect(spans[0].attributes['http.request.method']).toBe('GET');
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('should create span for POST requests with attributes', async () => {
|
|
56
|
-
app.use('*', honoTelemetryMiddleware());
|
|
57
|
-
app.post('/api/users', (c) => c.json({ id: '123' }, 201));
|
|
58
|
-
|
|
59
|
-
const response = await app.request('/api/users', {
|
|
60
|
-
method: 'POST',
|
|
61
|
-
headers: {
|
|
62
|
-
'Content-Type': 'application/json',
|
|
63
|
-
'User-Agent': 'test-agent',
|
|
64
|
-
},
|
|
65
|
-
body: JSON.stringify({ name: 'John' }),
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
expect(response.status).toBe(201);
|
|
69
|
-
const spans = exporter.getFinishedSpans();
|
|
70
|
-
expect(spans[0].attributes['http.request.method']).toBe('POST');
|
|
71
|
-
expect(spans[0].attributes['http.response.status_code']).toBe(201);
|
|
72
|
-
expect(spans[0].attributes['user_agent.original']).toBe('test-agent');
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it('should handle errors and record error status', async () => {
|
|
76
|
-
// Note: Hono catches errors internally before they propagate to middleware,
|
|
77
|
-
// so we can only detect the 500 status code, not record the actual exception
|
|
78
|
-
app.use('*', honoTelemetryMiddleware());
|
|
79
|
-
app.get('/api/error', () => {
|
|
80
|
-
throw new Error('Test error');
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
const response = await app.request('/api/error');
|
|
84
|
-
|
|
85
|
-
expect(response.status).toBe(500);
|
|
86
|
-
const spans = exporter.getFinishedSpans();
|
|
87
|
-
expect(spans).toHaveLength(1);
|
|
88
|
-
expect(spans[0].attributes['http.response.status_code']).toBe(500);
|
|
89
|
-
expect(spans[0].status.code).toBe(2); // SpanStatusCode.ERROR
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it('should record 4xx status codes', async () => {
|
|
93
|
-
app.use('*', honoTelemetryMiddleware());
|
|
94
|
-
app.get('/api/users/:id', (c) => c.json({ error: 'Not found' }, 404));
|
|
95
|
-
|
|
96
|
-
const response = await app.request('/api/users/999');
|
|
97
|
-
|
|
98
|
-
expect(response.status).toBe(404);
|
|
99
|
-
const spans = exporter.getFinishedSpans();
|
|
100
|
-
expect(spans[0].attributes['http.response.status_code']).toBe(404);
|
|
101
|
-
expect(spans[0].status.code).toBe(2); // SpanStatusCode.ERROR
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
describe('path filtering', () => {
|
|
106
|
-
it('should skip paths in ignorePaths (exact match)', async () => {
|
|
107
|
-
app.use(
|
|
108
|
-
'*',
|
|
109
|
-
honoTelemetryMiddleware({
|
|
110
|
-
ignorePaths: ['/health', '/metrics'],
|
|
111
|
-
}),
|
|
112
|
-
);
|
|
113
|
-
app.get('/health', (c) => c.json({ status: 'ok' }));
|
|
114
|
-
|
|
115
|
-
await app.request('/health');
|
|
116
|
-
|
|
117
|
-
expect(exporter.getFinishedSpans()).toHaveLength(0);
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it('should skip paths matching wildcard patterns', async () => {
|
|
121
|
-
app.use(
|
|
122
|
-
'*',
|
|
123
|
-
honoTelemetryMiddleware({
|
|
124
|
-
ignorePaths: ['/__telescope/*'],
|
|
125
|
-
}),
|
|
126
|
-
);
|
|
127
|
-
app.get('/__telescope/requests', (c) => c.json({ requests: [] }));
|
|
128
|
-
|
|
129
|
-
await app.request('/__telescope/requests');
|
|
130
|
-
|
|
131
|
-
expect(exporter.getFinishedSpans()).toHaveLength(0);
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it('should trace paths not in ignorePaths', async () => {
|
|
135
|
-
app.use(
|
|
136
|
-
'*',
|
|
137
|
-
honoTelemetryMiddleware({
|
|
138
|
-
ignorePaths: ['/health'],
|
|
139
|
-
}),
|
|
140
|
-
);
|
|
141
|
-
app.get('/api/users', (c) => c.json({ users: [] }));
|
|
142
|
-
|
|
143
|
-
await app.request('/api/users');
|
|
144
|
-
|
|
145
|
-
expect(exporter.getFinishedSpans()).toHaveLength(1);
|
|
146
|
-
});
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
describe('custom shouldSkip', () => {
|
|
150
|
-
it('should skip requests when shouldSkip returns true', async () => {
|
|
151
|
-
app.use(
|
|
152
|
-
'*',
|
|
153
|
-
honoTelemetryMiddleware({
|
|
154
|
-
shouldSkip: (c) => c.req.header('x-skip-trace') === 'true',
|
|
155
|
-
}),
|
|
156
|
-
);
|
|
157
|
-
app.get('/api/users', (c) => c.json({ users: [] }));
|
|
158
|
-
|
|
159
|
-
await app.request('/api/users', {
|
|
160
|
-
headers: { 'x-skip-trace': 'true' },
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
expect(exporter.getFinishedSpans()).toHaveLength(0);
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
it('should trace requests when shouldSkip returns false', async () => {
|
|
167
|
-
app.use(
|
|
168
|
-
'*',
|
|
169
|
-
honoTelemetryMiddleware({
|
|
170
|
-
shouldSkip: (c) => c.req.header('x-skip-trace') === 'true',
|
|
171
|
-
}),
|
|
172
|
-
);
|
|
173
|
-
app.get('/api/users', (c) => c.json({ users: [] }));
|
|
174
|
-
|
|
175
|
-
await app.request('/api/users');
|
|
176
|
-
|
|
177
|
-
expect(exporter.getFinishedSpans()).toHaveLength(1);
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
describe('user ID extraction', () => {
|
|
182
|
-
it('should extract user ID using custom function', async () => {
|
|
183
|
-
app.use(
|
|
184
|
-
'*',
|
|
185
|
-
honoTelemetryMiddleware({
|
|
186
|
-
getUserId: (c) => c.req.header('x-user-id'),
|
|
187
|
-
}),
|
|
188
|
-
);
|
|
189
|
-
app.get('/api/profile', (c) => c.json({ name: 'John' }));
|
|
190
|
-
|
|
191
|
-
await app.request('/api/profile', {
|
|
192
|
-
headers: { 'x-user-id': 'user-123' },
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
const spans = exporter.getFinishedSpans();
|
|
196
|
-
expect(spans[0].attributes['enduser.id']).toBe('user-123');
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
it('should not add user attribute when getUserId returns undefined', async () => {
|
|
200
|
-
app.use(
|
|
201
|
-
'*',
|
|
202
|
-
honoTelemetryMiddleware({
|
|
203
|
-
getUserId: () => undefined,
|
|
204
|
-
}),
|
|
205
|
-
);
|
|
206
|
-
app.get('/api/users', (c) => c.json({ users: [] }));
|
|
207
|
-
|
|
208
|
-
await app.request('/api/users');
|
|
209
|
-
|
|
210
|
-
const spans = exporter.getFinishedSpans();
|
|
211
|
-
expect(spans[0].attributes['enduser.id']).toBeUndefined();
|
|
212
|
-
});
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
describe('client IP extraction', () => {
|
|
216
|
-
it('should extract IP from x-forwarded-for header', async () => {
|
|
217
|
-
app.use('*', honoTelemetryMiddleware());
|
|
218
|
-
app.get('/api/users', (c) => c.json({ users: [] }));
|
|
219
|
-
|
|
220
|
-
await app.request('/api/users', {
|
|
221
|
-
headers: { 'x-forwarded-for': '192.168.1.1, 10.0.0.1' },
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
const spans = exporter.getFinishedSpans();
|
|
225
|
-
expect(spans[0].attributes['client.address']).toBe('192.168.1.1');
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
it('should extract IP from x-real-ip header', async () => {
|
|
229
|
-
app.use('*', honoTelemetryMiddleware());
|
|
230
|
-
app.get('/api/users', (c) => c.json({ users: [] }));
|
|
231
|
-
|
|
232
|
-
await app.request('/api/users', {
|
|
233
|
-
headers: { 'x-real-ip': '10.0.0.5' },
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
const spans = exporter.getFinishedSpans();
|
|
237
|
-
expect(spans[0].attributes['client.address']).toBe('10.0.0.5');
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
it('should extract IP from cf-connecting-ip header (Cloudflare)', async () => {
|
|
241
|
-
app.use('*', honoTelemetryMiddleware());
|
|
242
|
-
app.get('/api/users', (c) => c.json({ users: [] }));
|
|
243
|
-
|
|
244
|
-
await app.request('/api/users', {
|
|
245
|
-
headers: { 'cf-connecting-ip': '203.0.113.195' },
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
const spans = exporter.getFinishedSpans();
|
|
249
|
-
expect(spans[0].attributes['client.address']).toBe('203.0.113.195');
|
|
250
|
-
});
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
describe('trace context helpers', () => {
|
|
254
|
-
it('should provide trace context via getTraceContextFromHono', async () => {
|
|
255
|
-
let traceContext: any;
|
|
256
|
-
|
|
257
|
-
app.use('*', honoTelemetryMiddleware());
|
|
258
|
-
app.get('/api/users', (c) => {
|
|
259
|
-
traceContext = getTraceContextFromHono(c);
|
|
260
|
-
return c.json({ users: [] });
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
await app.request('/api/users');
|
|
264
|
-
|
|
265
|
-
expect(traceContext).toBeDefined();
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
it('should run function within span context via withHonoSpanContext', async () => {
|
|
269
|
-
let result: string | undefined;
|
|
270
|
-
|
|
271
|
-
app.use('*', honoTelemetryMiddleware());
|
|
272
|
-
app.get('/api/users', async (c) => {
|
|
273
|
-
result = await withHonoSpanContext(c, async () => 'executed');
|
|
274
|
-
return c.json({ users: [] });
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
await app.request('/api/users');
|
|
278
|
-
|
|
279
|
-
expect(result).toBe('executed');
|
|
280
|
-
});
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
describe('request ID extraction', () => {
|
|
284
|
-
it('should extract request ID from x-request-id header', async () => {
|
|
285
|
-
app.use('*', honoTelemetryMiddleware());
|
|
286
|
-
app.get('/api/users', (c) => c.json({ users: [] }));
|
|
287
|
-
|
|
288
|
-
await app.request('/api/users', {
|
|
289
|
-
headers: { 'x-request-id': 'req-abc123' },
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
const spans = exporter.getFinishedSpans();
|
|
293
|
-
expect(spans[0].attributes['http.request.id']).toBe('req-abc123');
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
it('should extract request ID from x-amzn-requestid header', async () => {
|
|
297
|
-
app.use('*', honoTelemetryMiddleware());
|
|
298
|
-
app.get('/api/users', (c) => c.json({ users: [] }));
|
|
299
|
-
|
|
300
|
-
await app.request('/api/users', {
|
|
301
|
-
headers: { 'x-amzn-requestid': 'amzn-req-456' },
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
const spans = exporter.getFinishedSpans();
|
|
305
|
-
expect(spans[0].attributes['http.request.id']).toBe('amzn-req-456');
|
|
306
|
-
});
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
describe('URL attributes', () => {
|
|
310
|
-
it('should capture full URL and path', async () => {
|
|
311
|
-
app.use('*', honoTelemetryMiddleware());
|
|
312
|
-
app.get('/api/users', (c) => c.json({ users: [] }));
|
|
313
|
-
|
|
314
|
-
await app.request('http://localhost/api/users?page=1');
|
|
315
|
-
|
|
316
|
-
const spans = exporter.getFinishedSpans();
|
|
317
|
-
expect(spans[0].attributes['url.full']).toContain('/api/users');
|
|
318
|
-
expect(spans[0].attributes['url.path']).toBe('/api/users');
|
|
319
|
-
expect(spans[0].attributes['server.address']).toBe('localhost');
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
it('should capture scheme', async () => {
|
|
323
|
-
app.use('*', honoTelemetryMiddleware());
|
|
324
|
-
app.get('/api/users', (c) => c.json({ users: [] }));
|
|
325
|
-
|
|
326
|
-
await app.request('https://api.example.com/api/users');
|
|
327
|
-
|
|
328
|
-
const spans = exporter.getFinishedSpans();
|
|
329
|
-
expect(spans[0].attributes['url.scheme']).toBe('https');
|
|
330
|
-
});
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
describe('multiple requests', () => {
|
|
334
|
-
it('should create separate spans for each request', async () => {
|
|
335
|
-
app.use('*', honoTelemetryMiddleware());
|
|
336
|
-
app.get('/api/users', (c) => c.json({ users: [] }));
|
|
337
|
-
app.post('/api/users', (c) => c.json({ id: '1' }, 201));
|
|
338
|
-
|
|
339
|
-
await app.request('/api/users');
|
|
340
|
-
await app.request('/api/users', { method: 'POST' });
|
|
341
|
-
|
|
342
|
-
const spans = exporter.getFinishedSpans();
|
|
343
|
-
expect(spans).toHaveLength(2);
|
|
344
|
-
expect(spans[0].attributes['http.request.method']).toBe('GET');
|
|
345
|
-
expect(spans[1].attributes['http.request.method']).toBe('POST');
|
|
346
|
-
});
|
|
347
|
-
});
|
|
348
|
-
});
|