@geekmidas/telescope 9.0.2 → 10.0.0-alpha.1

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.
Files changed (122) hide show
  1. package/README.md +14 -4
  2. package/dist/{http-D5kn3_Mt.d.mts → http-BiNtg85L.d.mts} +3 -3
  3. package/dist/{http-D5kn3_Mt.d.mts.map → http-BiNtg85L.d.mts.map} +1 -1
  4. package/dist/instrumentation/http.d.mts +1 -1
  5. package/dist/instrumentation/index.cjs +2 -2
  6. package/dist/instrumentation/index.d.cts +2 -2
  7. package/dist/instrumentation/index.d.mts +3 -3
  8. package/dist/instrumentation/index.mjs +2 -2
  9. package/dist/instrumentation/middleware.cjs +1 -1
  10. package/dist/instrumentation/middleware.d.cts +1 -1
  11. package/dist/instrumentation/middleware.d.mts +1 -1
  12. package/dist/instrumentation/middleware.mjs +1 -1
  13. package/dist/instrumentation/otel.cjs +1 -1
  14. package/dist/instrumentation/otel.d.cts +1 -1
  15. package/dist/instrumentation/otel.d.mts +1 -1
  16. package/dist/instrumentation/otel.mjs +1 -1
  17. package/dist/logger/console.d.cts.map +1 -1
  18. package/dist/logger/pino.d.mts +2 -2
  19. package/dist/{middleware-DqPQpcup.cjs → middleware-Bcq6XXGG.cjs} +2 -2
  20. package/dist/middleware-Bcq6XXGG.cjs.map +1 -0
  21. package/dist/{middleware-4-GrQlOT.mjs → middleware-D1XEHvyy.mjs} +2 -2
  22. package/dist/middleware-D1XEHvyy.mjs.map +1 -0
  23. package/dist/{middleware-CA84nsNN.d.cts → middleware-PeyS-9Ny.d.cts} +2 -2
  24. package/dist/{middleware-CA84nsNN.d.cts.map → middleware-PeyS-9Ny.d.cts.map} +1 -1
  25. package/dist/{middleware-DAceVpfw.d.mts → middleware-oHvo_ZgU.d.mts} +2 -2
  26. package/dist/{middleware-DAceVpfw.d.mts.map → middleware-oHvo_ZgU.d.mts.map} +1 -1
  27. package/dist/{otel-40udNP5g.mjs → otel-B9bNoeZ2.mjs} +3 -3
  28. package/dist/otel-B9bNoeZ2.mjs.map +1 -0
  29. package/dist/{otel-SBVoVEHr.d.cts → otel-Bk89b1lX.d.cts} +3 -3
  30. package/dist/{otel-1bJPb2Z7.d.mts.map → otel-Bk89b1lX.d.cts.map} +1 -1
  31. package/dist/{otel-COrpz_GZ.cjs → otel-BzjOtVxZ.cjs} +3 -3
  32. package/dist/otel-BzjOtVxZ.cjs.map +1 -0
  33. package/dist/{otel-1bJPb2Z7.d.mts → otel-lPMsM26e.d.mts} +3 -3
  34. package/dist/{otel-SBVoVEHr.d.cts.map → otel-lPMsM26e.d.mts.map} +1 -1
  35. package/dist/storage/kysely.cjs +48 -38
  36. package/dist/storage/kysely.cjs.map +1 -1
  37. package/dist/storage/kysely.d.cts +29 -9
  38. package/dist/storage/kysely.d.cts.map +1 -1
  39. package/dist/storage/kysely.d.mts +29 -9
  40. package/dist/storage/kysely.d.mts.map +1 -1
  41. package/dist/storage/kysely.mjs +48 -38
  42. package/dist/storage/kysely.mjs.map +1 -1
  43. package/dist/tracing-PnECEbEu.d.mts.map +1 -1
  44. package/package.json +6 -3
  45. package/CHANGELOG.md +0 -72
  46. package/dist/middleware-4-GrQlOT.mjs.map +0 -1
  47. package/dist/middleware-DqPQpcup.cjs.map +0 -1
  48. package/dist/otel-40udNP5g.mjs.map +0 -1
  49. package/dist/otel-COrpz_GZ.cjs.map +0 -1
  50. package/src/Telescope.ts +0 -561
  51. package/src/__tests__/Telescope.spec.ts +0 -438
  52. package/src/__tests__/pino-transport.spec.ts +0 -115
  53. package/src/__tests__/redact.spec.ts +0 -336
  54. package/src/adapters/__tests__/lambda.spec.ts +0 -318
  55. package/src/adapters/__tests__/middy.spec.ts +0 -741
  56. package/src/adapters/index.ts +0 -22
  57. package/src/adapters/lambda.ts +0 -432
  58. package/src/adapters/middy.ts +0 -402
  59. package/src/adapters/types.ts +0 -181
  60. package/src/core.ts +0 -51
  61. package/src/index.ts +0 -34
  62. package/src/instrumentation/__tests__/core.spec.ts +0 -248
  63. package/src/instrumentation/__tests__/hono.spec.ts +0 -348
  64. package/src/instrumentation/__tests__/middleware.spec.ts +0 -528
  65. package/src/instrumentation/__tests__/otel.spec.ts +0 -291
  66. package/src/instrumentation/__tests__/tracing.spec.ts +0 -208
  67. package/src/instrumentation/core.ts +0 -228
  68. package/src/instrumentation/hono.ts +0 -270
  69. package/src/instrumentation/http.ts +0 -306
  70. package/src/instrumentation/index.ts +0 -90
  71. package/src/instrumentation/middleware.ts +0 -297
  72. package/src/instrumentation/otel.ts +0 -271
  73. package/src/instrumentation/setup.ts +0 -246
  74. package/src/instrumentation/tracing.ts +0 -145
  75. package/src/logger/__tests__/console.spec.ts +0 -366
  76. package/src/logger/__tests__/pino.spec.ts +0 -217
  77. package/src/logger/console.ts +0 -230
  78. package/src/logger/pino.ts +0 -201
  79. package/src/metrics/MetricsAggregator.ts +0 -555
  80. package/src/metrics/__tests__/MetricsAggregator.spec.ts +0 -298
  81. package/src/metrics/index.ts +0 -2
  82. package/src/metrics/storage/InMemoryMetricsStorage.ts +0 -380
  83. package/src/metrics/storage/MetricsStorage.ts +0 -74
  84. package/src/metrics/storage/index.ts +0 -3
  85. package/src/metrics/storage/types.ts +0 -243
  86. package/src/otlp/__tests__/receiver.spec.ts +0 -367
  87. package/src/otlp/__tests__/transformer.spec.ts +0 -455
  88. package/src/otlp/hono.ts +0 -120
  89. package/src/otlp/index.ts +0 -22
  90. package/src/otlp/receiver.ts +0 -162
  91. package/src/otlp/transformer.ts +0 -367
  92. package/src/otlp/types.ts +0 -364
  93. package/src/redact.ts +0 -116
  94. package/src/server/__tests__/hono.spec.ts +0 -814
  95. package/src/server/hono.ts +0 -442
  96. package/src/storage/__tests__/kysely.spec.ts +0 -715
  97. package/src/storage/__tests__/memory.spec.ts +0 -694
  98. package/src/storage/kysely.ts +0 -599
  99. package/src/storage/memory.ts +0 -204
  100. package/src/types.ts +0 -447
  101. package/src/ui-assets.ts +0 -40
  102. package/tsconfig.json +0 -9
  103. package/ui/CHANGELOG.md +0 -7
  104. package/ui/index.html +0 -12
  105. package/ui/node_modules/.bin/tsc +0 -21
  106. package/ui/node_modules/.bin/tsserver +0 -21
  107. package/ui/node_modules/.bin/vite +0 -21
  108. package/ui/package.json +0 -24
  109. package/ui/src/App.tsx +0 -595
  110. package/ui/src/api.ts +0 -88
  111. package/ui/src/components/ExceptionDetail.tsx +0 -100
  112. package/ui/src/components/FilterBar.tsx +0 -162
  113. package/ui/src/components/LogDetail.tsx +0 -91
  114. package/ui/src/components/RequestDetail.tsx +0 -143
  115. package/ui/src/main.tsx +0 -10
  116. package/ui/src/styles.css +0 -11
  117. package/ui/src/types.ts +0 -63
  118. package/ui/src/vite-env.d.ts +0 -1
  119. package/ui/src/vite-plugin-gkm-config.ts +0 -54
  120. package/ui/tsconfig.json +0 -20
  121. package/ui/tsconfig.tsbuildinfo +0 -1
  122. 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
- });