@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.
Files changed (167) hide show
  1. package/README.md +14 -4
  2. package/dist/{hono-Bz9PQQzj.mjs → hono-Bw-ey6CT.mjs} +2 -2
  3. package/dist/{hono-Bz9PQQzj.mjs.map → hono-Bw-ey6CT.mjs.map} +1 -1
  4. package/dist/{hono-Z_JIICmt.cjs → hono-CPHX7FAm.cjs} +2 -2
  5. package/dist/{hono-Z_JIICmt.cjs.map → hono-CPHX7FAm.cjs.map} +1 -1
  6. package/dist/{http-Crsriv32.mjs → http-BIvr0YdI.mjs} +1 -1
  7. package/dist/{http-Crsriv32.mjs.map → http-BIvr0YdI.mjs.map} +1 -1
  8. package/dist/{http-JycFRfSe.cjs → http-BsqmhurZ.cjs} +1 -1
  9. package/dist/{http-JycFRfSe.cjs.map → http-BsqmhurZ.cjs.map} +1 -1
  10. package/dist/{http-D5kn3_Mt.d.mts → http-DULM1u2L.d.cts} +3 -3
  11. package/dist/{http-D5kn3_Mt.d.mts.map → http-DULM1u2L.d.cts.map} +1 -1
  12. package/dist/{http-Dvca6fC3.d.cts → http-GB-Nlvwc.d.mts} +3 -3
  13. package/dist/{http-Dvca6fC3.d.cts.map → http-GB-Nlvwc.d.mts.map} +1 -1
  14. package/dist/instrumentation/hono.cjs +2 -2
  15. package/dist/instrumentation/hono.mjs +2 -2
  16. package/dist/instrumentation/http.cjs +1 -1
  17. package/dist/instrumentation/http.d.cts +1 -1
  18. package/dist/instrumentation/http.d.mts +1 -1
  19. package/dist/instrumentation/http.mjs +1 -1
  20. package/dist/instrumentation/index.cjs +5 -5
  21. package/dist/instrumentation/index.d.cts +3 -3
  22. package/dist/instrumentation/index.d.mts +3 -3
  23. package/dist/instrumentation/index.mjs +5 -5
  24. package/dist/instrumentation/middleware.cjs +2 -2
  25. package/dist/instrumentation/middleware.d.cts +1 -1
  26. package/dist/instrumentation/middleware.d.mts +1 -1
  27. package/dist/instrumentation/middleware.mjs +2 -2
  28. package/dist/instrumentation/otel.cjs +2 -2
  29. package/dist/instrumentation/otel.d.cts +1 -1
  30. package/dist/instrumentation/otel.d.mts +1 -1
  31. package/dist/instrumentation/otel.mjs +2 -2
  32. package/dist/instrumentation/setup.cjs +1 -1
  33. package/dist/instrumentation/setup.mjs +1 -1
  34. package/dist/logger/console.d.cts.map +1 -1
  35. package/dist/{middleware-D3v5SnSs.cjs → middleware-Bcq6XXGG.cjs} +3 -3
  36. package/dist/middleware-Bcq6XXGG.cjs.map +1 -0
  37. package/dist/{middleware-Dy1ctr2N.mjs → middleware-D1XEHvyy.mjs} +3 -3
  38. package/dist/middleware-D1XEHvyy.mjs.map +1 -0
  39. package/dist/{middleware-CA84nsNN.d.cts → middleware-PeyS-9Ny.d.cts} +2 -2
  40. package/dist/{middleware-CA84nsNN.d.cts.map → middleware-PeyS-9Ny.d.cts.map} +1 -1
  41. package/dist/{middleware-DAceVpfw.d.mts → middleware-oHvo_ZgU.d.mts} +2 -2
  42. package/dist/{middleware-DAceVpfw.d.mts.map → middleware-oHvo_ZgU.d.mts.map} +1 -1
  43. package/dist/{otel-DPKNyJ0t.mjs → otel-B9bNoeZ2.mjs} +4 -4
  44. package/dist/otel-B9bNoeZ2.mjs.map +1 -0
  45. package/dist/{otel-1bJPb2Z7.d.mts → otel-Bk89b1lX.d.cts} +3 -3
  46. package/dist/{otel-1bJPb2Z7.d.mts.map → otel-Bk89b1lX.d.cts.map} +1 -1
  47. package/dist/{otel-DuiBeBPU.cjs → otel-BzjOtVxZ.cjs} +4 -4
  48. package/dist/otel-BzjOtVxZ.cjs.map +1 -0
  49. package/dist/{otel-SBVoVEHr.d.cts → otel-lPMsM26e.d.mts} +3 -3
  50. package/dist/{otel-SBVoVEHr.d.cts.map → otel-lPMsM26e.d.mts.map} +1 -1
  51. package/dist/otlp/hono.cjs +3 -3
  52. package/dist/otlp/hono.mjs +3 -3
  53. package/dist/otlp/index.cjs +3 -3
  54. package/dist/otlp/index.mjs +3 -3
  55. package/dist/otlp/receiver.cjs +3 -3
  56. package/dist/otlp/receiver.mjs +3 -3
  57. package/dist/otlp/transformer.cjs +2 -2
  58. package/dist/otlp/transformer.mjs +2 -2
  59. package/dist/otlp/types.cjs +1 -1
  60. package/dist/otlp/types.mjs +1 -1
  61. package/dist/{receiver-q4f7eirs.mjs → receiver-D46dfkYI.mjs} +2 -2
  62. package/dist/{receiver-q4f7eirs.mjs.map → receiver-D46dfkYI.mjs.map} +1 -1
  63. package/dist/{receiver-BW_4go_X.cjs → receiver-slCoAFat.cjs} +2 -2
  64. package/dist/{receiver-BW_4go_X.cjs.map → receiver-slCoAFat.cjs.map} +1 -1
  65. package/dist/{setup-BhCp2LUp.mjs → setup-DC9JMEYZ.mjs} +1 -1
  66. package/dist/{setup-BhCp2LUp.mjs.map → setup-DC9JMEYZ.mjs.map} +1 -1
  67. package/dist/{setup-_uT4L_x5.cjs → setup-DufxQpi9.cjs} +1 -1
  68. package/dist/{setup-_uT4L_x5.cjs.map → setup-DufxQpi9.cjs.map} +1 -1
  69. package/dist/storage/kysely.cjs +48 -38
  70. package/dist/storage/kysely.cjs.map +1 -1
  71. package/dist/storage/kysely.d.cts +29 -9
  72. package/dist/storage/kysely.d.cts.map +1 -1
  73. package/dist/storage/kysely.d.mts +29 -9
  74. package/dist/storage/kysely.d.mts.map +1 -1
  75. package/dist/storage/kysely.mjs +48 -38
  76. package/dist/storage/kysely.mjs.map +1 -1
  77. package/dist/tracing-BX8uY2nR.d.cts.map +1 -1
  78. package/dist/tracing-PnECEbEu.d.mts.map +1 -1
  79. package/dist/{transformer-CFrJQkUg.mjs → transformer-CFQ4WFk6.mjs} +2 -2
  80. package/dist/{transformer-CFrJQkUg.mjs.map → transformer-CFQ4WFk6.mjs.map} +1 -1
  81. package/dist/{transformer-DvDYyFRQ.cjs → transformer-Csl6fbgv.cjs} +2 -2
  82. package/dist/{transformer-DvDYyFRQ.cjs.map → transformer-Csl6fbgv.cjs.map} +1 -1
  83. package/dist/types-B1EIa_cQ.d.mts.map +1 -1
  84. package/dist/{types-BBeTDpHr.cjs → types-BMiHgsi4.cjs} +1 -1
  85. package/dist/{types-BBeTDpHr.cjs.map → types-BMiHgsi4.cjs.map} +1 -1
  86. package/dist/{types-CXYLhG_G.mjs → types-Cj59nqy7.mjs} +1 -1
  87. package/dist/{types-CXYLhG_G.mjs.map → types-Cj59nqy7.mjs.map} +1 -1
  88. package/dist/types-l7Rlb5Mg.d.cts.map +1 -1
  89. package/package.json +6 -3
  90. package/CHANGELOG.md +0 -51
  91. package/dist/middleware-D3v5SnSs.cjs.map +0 -1
  92. package/dist/middleware-Dy1ctr2N.mjs.map +0 -1
  93. package/dist/otel-DPKNyJ0t.mjs.map +0 -1
  94. package/dist/otel-DuiBeBPU.cjs.map +0 -1
  95. package/src/Telescope.ts +0 -561
  96. package/src/__tests__/Telescope.spec.ts +0 -438
  97. package/src/__tests__/pino-transport.spec.ts +0 -115
  98. package/src/__tests__/redact.spec.ts +0 -336
  99. package/src/adapters/__tests__/lambda.spec.ts +0 -318
  100. package/src/adapters/__tests__/middy.spec.ts +0 -741
  101. package/src/adapters/index.ts +0 -22
  102. package/src/adapters/lambda.ts +0 -432
  103. package/src/adapters/middy.ts +0 -402
  104. package/src/adapters/types.ts +0 -181
  105. package/src/core.ts +0 -51
  106. package/src/index.ts +0 -34
  107. package/src/instrumentation/__tests__/core.spec.ts +0 -248
  108. package/src/instrumentation/__tests__/hono.spec.ts +0 -348
  109. package/src/instrumentation/__tests__/middleware.spec.ts +0 -528
  110. package/src/instrumentation/__tests__/otel.spec.ts +0 -291
  111. package/src/instrumentation/__tests__/tracing.spec.ts +0 -208
  112. package/src/instrumentation/core.ts +0 -228
  113. package/src/instrumentation/hono.ts +0 -270
  114. package/src/instrumentation/http.ts +0 -306
  115. package/src/instrumentation/index.ts +0 -90
  116. package/src/instrumentation/middleware.ts +0 -297
  117. package/src/instrumentation/otel.ts +0 -271
  118. package/src/instrumentation/setup.ts +0 -246
  119. package/src/instrumentation/tracing.ts +0 -145
  120. package/src/logger/__tests__/console.spec.ts +0 -366
  121. package/src/logger/__tests__/pino.spec.ts +0 -217
  122. package/src/logger/console.ts +0 -230
  123. package/src/logger/pino.ts +0 -201
  124. package/src/metrics/MetricsAggregator.ts +0 -555
  125. package/src/metrics/__tests__/MetricsAggregator.spec.ts +0 -298
  126. package/src/metrics/index.ts +0 -2
  127. package/src/metrics/storage/InMemoryMetricsStorage.ts +0 -380
  128. package/src/metrics/storage/MetricsStorage.ts +0 -74
  129. package/src/metrics/storage/index.ts +0 -3
  130. package/src/metrics/storage/types.ts +0 -243
  131. package/src/otlp/__tests__/receiver.spec.ts +0 -367
  132. package/src/otlp/__tests__/transformer.spec.ts +0 -455
  133. package/src/otlp/hono.ts +0 -120
  134. package/src/otlp/index.ts +0 -22
  135. package/src/otlp/receiver.ts +0 -162
  136. package/src/otlp/transformer.ts +0 -367
  137. package/src/otlp/types.ts +0 -364
  138. package/src/redact.ts +0 -116
  139. package/src/server/__tests__/hono.spec.ts +0 -814
  140. package/src/server/hono.ts +0 -442
  141. package/src/storage/__tests__/kysely.spec.ts +0 -715
  142. package/src/storage/__tests__/memory.spec.ts +0 -694
  143. package/src/storage/kysely.ts +0 -599
  144. package/src/storage/memory.ts +0 -204
  145. package/src/types.ts +0 -447
  146. package/src/ui-assets.ts +0 -40
  147. package/tsconfig.json +0 -9
  148. package/ui/CHANGELOG.md +0 -7
  149. package/ui/index.html +0 -12
  150. package/ui/node_modules/.bin/tsc +0 -21
  151. package/ui/node_modules/.bin/tsserver +0 -21
  152. package/ui/node_modules/.bin/vite +0 -21
  153. package/ui/package.json +0 -24
  154. package/ui/src/App.tsx +0 -595
  155. package/ui/src/api.ts +0 -88
  156. package/ui/src/components/ExceptionDetail.tsx +0 -100
  157. package/ui/src/components/FilterBar.tsx +0 -162
  158. package/ui/src/components/LogDetail.tsx +0 -91
  159. package/ui/src/components/RequestDetail.tsx +0 -143
  160. package/ui/src/main.tsx +0 -10
  161. package/ui/src/styles.css +0 -11
  162. package/ui/src/types.ts +0 -63
  163. package/ui/src/vite-env.d.ts +0 -1
  164. package/ui/src/vite-plugin-gkm-config.ts +0 -54
  165. package/ui/tsconfig.json +0 -20
  166. package/ui/tsconfig.tsbuildinfo +0 -1
  167. package/ui/vite.config.ts +0 -13
@@ -1,528 +0,0 @@
1
- import { trace } from '@opentelemetry/api';
2
- import {
3
- BasicTracerProvider,
4
- InMemorySpanExporter,
5
- SimpleSpanProcessor,
6
- } from '@opentelemetry/sdk-trace-base';
7
- import type { Context as LambdaContext } from 'aws-lambda';
8
- import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
9
- import {
10
- getContextFromEvent,
11
- getSpanFromEvent,
12
- telemetryMiddleware,
13
- withEventContext,
14
- } from '../middleware';
15
-
16
- describe('telemetryMiddleware', () => {
17
- let mockContext: LambdaContext;
18
- let middleware: ReturnType<typeof telemetryMiddleware>;
19
- let exporter: InMemorySpanExporter;
20
- let provider: BasicTracerProvider;
21
-
22
- beforeEach(() => {
23
- // Set up real OpenTelemetry with in-memory exporter
24
- exporter = new InMemorySpanExporter();
25
- provider = new BasicTracerProvider();
26
- provider.addSpanProcessor(new SimpleSpanProcessor(exporter));
27
- provider.register();
28
-
29
- mockContext = {
30
- callbackWaitsForEmptyEventLoop: true,
31
- functionName: 'test-function',
32
- functionVersion: '1',
33
- invokedFunctionArn: 'arn:aws:lambda:us-east-1:123456789:function:test',
34
- memoryLimitInMB: '256',
35
- awsRequestId: 'test-request-id-123',
36
- logGroupName: '/aws/lambda/test',
37
- logStreamName: '2024/01/01/[$LATEST]abc123',
38
- getRemainingTimeInMillis: () => 10000,
39
- done: vi.fn(),
40
- fail: vi.fn(),
41
- succeed: vi.fn(),
42
- };
43
-
44
- middleware = telemetryMiddleware();
45
- });
46
-
47
- afterEach(async () => {
48
- exporter.reset();
49
- await provider.shutdown();
50
- // Reset global tracer provider
51
- trace.disable();
52
- });
53
-
54
- describe('before hook', () => {
55
- it('should create span for API Gateway v2 event', async () => {
56
- const event = {
57
- rawPath: '/api/users',
58
- headers: {
59
- host: 'api.example.com',
60
- 'content-type': 'application/json',
61
- },
62
- requestContext: {
63
- requestId: 'req-123',
64
- http: {
65
- method: 'POST',
66
- sourceIp: '192.168.1.1',
67
- userAgent: 'test-agent',
68
- path: '/api/users',
69
- },
70
- },
71
- body: JSON.stringify({ name: 'John' }),
72
- routeKey: 'POST /api/users',
73
- };
74
-
75
- const request = { event, context: mockContext, response: null };
76
- await middleware.before?.(request as any);
77
-
78
- // Span should be stored on event
79
- const span = getSpanFromEvent(event);
80
- expect(span).toBeDefined();
81
- expect(getContextFromEvent(event)).toBeDefined();
82
-
83
- // End span to export it
84
- const response = { statusCode: 201, body: '{"id":"123"}' };
85
- await middleware.after?.({
86
- event,
87
- context: mockContext,
88
- response,
89
- } as any);
90
-
91
- // Check exported spans
92
- const spans = exporter.getFinishedSpans();
93
- expect(spans).toHaveLength(1);
94
- expect(spans[0].name).toBe('POST /api/users');
95
- expect(spans[0].attributes['http.request.method']).toBe('POST');
96
- expect(spans[0].attributes['url.path']).toBe('/api/users');
97
- expect(spans[0].attributes['client.address']).toBe('192.168.1.1');
98
- });
99
-
100
- it('should create span for API Gateway v1 event', async () => {
101
- const event = {
102
- httpMethod: 'GET',
103
- path: '/api/items',
104
- headers: {
105
- Host: 'api.example.com',
106
- 'User-Agent': 'test-agent',
107
- },
108
- requestContext: {
109
- requestId: 'req-456',
110
- identity: {
111
- sourceIp: '10.0.0.1',
112
- },
113
- },
114
- resource: '/api/items/{id}',
115
- queryStringParameters: { page: '1' },
116
- };
117
-
118
- const request = { event, context: mockContext, response: null };
119
- await middleware.before?.(request as any);
120
-
121
- expect(getSpanFromEvent(event)).toBeDefined();
122
-
123
- await middleware.after?.({
124
- event,
125
- context: mockContext,
126
- response: { statusCode: 200 },
127
- } as any);
128
-
129
- const spans = exporter.getFinishedSpans();
130
- expect(spans).toHaveLength(1);
131
- expect(spans[0].name).toBe('GET /api/items/{id}');
132
- expect(spans[0].attributes['http.request.method']).toBe('GET');
133
- expect(spans[0].attributes['http.route']).toBe('/api/items/{id}');
134
- });
135
-
136
- it('should handle generic Lambda invocation', async () => {
137
- const event = {
138
- customField: 'value',
139
- headers: {},
140
- };
141
-
142
- const request = { event, context: mockContext, response: null };
143
- await middleware.before?.(request as any);
144
-
145
- expect(getSpanFromEvent(event)).toBeDefined();
146
-
147
- await middleware.after?.({
148
- event,
149
- context: mockContext,
150
- response: { statusCode: 200 },
151
- } as any);
152
-
153
- const spans = exporter.getFinishedSpans();
154
- expect(spans).toHaveLength(1);
155
- expect(spans[0].attributes['http.request.method']).toBe('INVOKE');
156
- });
157
-
158
- it('should skip tracing when shouldSkip returns true', async () => {
159
- const skipMiddleware = telemetryMiddleware({
160
- shouldSkip: (event) => event.skipTrace === true,
161
- });
162
-
163
- const event = {
164
- skipTrace: true,
165
- headers: {},
166
- };
167
-
168
- const request = { event, context: mockContext, response: null };
169
- await skipMiddleware.before?.(request as any);
170
-
171
- expect(getSpanFromEvent(event)).toBeUndefined();
172
- expect(exporter.getFinishedSpans()).toHaveLength(0);
173
- });
174
-
175
- it('should record request body when recordBody is true', async () => {
176
- const bodyMiddleware = telemetryMiddleware({ recordBody: true });
177
- const bodyContent = JSON.stringify({
178
- name: 'John',
179
- email: 'john@example.com',
180
- });
181
-
182
- const event = {
183
- httpMethod: 'POST',
184
- path: '/api/users',
185
- headers: {},
186
- body: bodyContent,
187
- };
188
-
189
- const request = { event, context: mockContext, response: null };
190
- await bodyMiddleware.before?.(request as any);
191
- await bodyMiddleware.after?.({
192
- event,
193
- context: mockContext,
194
- response: { statusCode: 201 },
195
- } as any);
196
-
197
- const spans = exporter.getFinishedSpans();
198
- expect(spans[0].attributes['http.request.body']).toBe(bodyContent);
199
- });
200
-
201
- it('should truncate large request bodies', async () => {
202
- const bodyMiddleware = telemetryMiddleware({ recordBody: true });
203
- const largeBody = 'x'.repeat(5000);
204
-
205
- const event = {
206
- httpMethod: 'POST',
207
- path: '/api/upload',
208
- headers: {},
209
- body: largeBody,
210
- };
211
-
212
- const request = { event, context: mockContext, response: null };
213
- await bodyMiddleware.before?.(request as any);
214
- await bodyMiddleware.after?.({
215
- event,
216
- context: mockContext,
217
- response: { statusCode: 200 },
218
- } as any);
219
-
220
- const spans = exporter.getFinishedSpans();
221
- const recordedBody = spans[0].attributes['http.request.body'] as string;
222
- expect(recordedBody.length).toBeLessThan(largeBody.length);
223
- expect(recordedBody).toContain('...');
224
- });
225
-
226
- it('should add Lambda-specific attributes', async () => {
227
- const event = {
228
- httpMethod: 'GET',
229
- path: '/api/test',
230
- headers: {},
231
- };
232
-
233
- const request = { event, context: mockContext, response: null };
234
- await middleware.before?.(request as any);
235
- await middleware.after?.({
236
- event,
237
- context: mockContext,
238
- response: { statusCode: 200 },
239
- } as any);
240
-
241
- const spans = exporter.getFinishedSpans();
242
- expect(spans[0].attributes['faas.invocation_id']).toBe(
243
- 'test-request-id-123',
244
- );
245
- expect(spans[0].attributes['faas.name']).toBe('test-function');
246
- });
247
- });
248
-
249
- describe('after hook', () => {
250
- it('should record success status code', async () => {
251
- const event = {
252
- httpMethod: 'GET',
253
- path: '/api/users',
254
- headers: {},
255
- };
256
-
257
- await middleware.before?.({
258
- event,
259
- context: mockContext,
260
- response: null,
261
- } as any);
262
- await middleware.after?.({
263
- event,
264
- context: mockContext,
265
- response: { statusCode: 200, body: '{"users":[]}' },
266
- } as any);
267
-
268
- const spans = exporter.getFinishedSpans();
269
- expect(spans[0].attributes['http.response.status_code']).toBe(200);
270
- expect(spans[0].status.code).toBe(1); // SpanStatusCode.OK
271
- });
272
-
273
- it('should record 4xx status as error', async () => {
274
- const event = {
275
- httpMethod: 'GET',
276
- path: '/api/users/999',
277
- headers: {},
278
- };
279
-
280
- await middleware.before?.({
281
- event,
282
- context: mockContext,
283
- response: null,
284
- } as any);
285
- await middleware.after?.({
286
- event,
287
- context: mockContext,
288
- response: { statusCode: 404, body: '{"error":"Not found"}' },
289
- } as any);
290
-
291
- const spans = exporter.getFinishedSpans();
292
- expect(spans[0].attributes['http.response.status_code']).toBe(404);
293
- expect(spans[0].status.code).toBe(2); // SpanStatusCode.ERROR
294
- });
295
-
296
- it('should record response body when recordResponseBody is true', async () => {
297
- const responseMiddleware = telemetryMiddleware({
298
- recordResponseBody: true,
299
- });
300
- const responseBody = '{"users":[{"id":"1","name":"John"}]}';
301
-
302
- const event = {
303
- httpMethod: 'GET',
304
- path: '/api/users',
305
- headers: {},
306
- };
307
-
308
- await responseMiddleware.before?.({
309
- event,
310
- context: mockContext,
311
- response: null,
312
- } as any);
313
- await responseMiddleware.after?.({
314
- event,
315
- context: mockContext,
316
- response: { statusCode: 200, body: responseBody },
317
- } as any);
318
-
319
- const spans = exporter.getFinishedSpans();
320
- expect(spans[0].attributes['http.response.body']).toBe(responseBody);
321
- });
322
- });
323
-
324
- describe('onError hook', () => {
325
- it('should record error with exception', async () => {
326
- const event = {
327
- httpMethod: 'GET',
328
- path: '/api/error',
329
- headers: {},
330
- };
331
-
332
- await middleware.before?.({
333
- event,
334
- context: mockContext,
335
- response: null,
336
- } as any);
337
-
338
- const error = new Error('Database connection failed');
339
- await middleware.onError?.({
340
- event,
341
- context: mockContext,
342
- response: { statusCode: 500 },
343
- error,
344
- } as any);
345
-
346
- const spans = exporter.getFinishedSpans();
347
- expect(spans[0].attributes['http.response.status_code']).toBe(500);
348
- expect(spans[0].status.code).toBe(2); // SpanStatusCode.ERROR
349
- expect(spans[0].events).toHaveLength(1);
350
- expect(spans[0].events[0].name).toBe('exception');
351
- });
352
-
353
- it('should use error statusCode if available', async () => {
354
- const event = {
355
- httpMethod: 'GET',
356
- path: '/api/protected',
357
- headers: {},
358
- };
359
-
360
- await middleware.before?.({
361
- event,
362
- context: mockContext,
363
- response: null,
364
- } as any);
365
-
366
- const error = Object.assign(new Error('Forbidden'), { statusCode: 403 });
367
- await middleware.onError?.({
368
- event,
369
- context: mockContext,
370
- response: null,
371
- error,
372
- } as any);
373
-
374
- const spans = exporter.getFinishedSpans();
375
- expect(spans[0].attributes['http.response.status_code']).toBe(403);
376
- });
377
- });
378
-
379
- describe('withEventContext', () => {
380
- it('should run function within span context', async () => {
381
- const event = {
382
- httpMethod: 'GET',
383
- path: '/api/users',
384
- headers: {},
385
- };
386
-
387
- await middleware.before?.({
388
- event,
389
- context: mockContext,
390
- response: null,
391
- } as any);
392
-
393
- const result = await withEventContext(event, async () => {
394
- return 'executed-in-context';
395
- });
396
-
397
- expect(result).toBe('executed-in-context');
398
- });
399
-
400
- it('should run function normally when no context exists', async () => {
401
- const event = {};
402
-
403
- const result = await withEventContext(event, async () => {
404
- return 'executed-without-context';
405
- });
406
-
407
- expect(result).toBe('executed-without-context');
408
- });
409
- });
410
-
411
- describe('custom extractors', () => {
412
- it('should extract user ID using getUserId', async () => {
413
- const userMiddleware = telemetryMiddleware({
414
- getUserId: (event) => event.requestContext?.authorizer?.userId,
415
- });
416
-
417
- const event = {
418
- httpMethod: 'GET',
419
- path: '/api/profile',
420
- headers: {},
421
- requestContext: {
422
- authorizer: {
423
- userId: 'user-abc123',
424
- },
425
- },
426
- };
427
-
428
- await userMiddleware.before?.({
429
- event,
430
- context: mockContext,
431
- response: null,
432
- } as any);
433
- await userMiddleware.after?.({
434
- event,
435
- context: mockContext,
436
- response: { statusCode: 200 },
437
- } as any);
438
-
439
- const spans = exporter.getFinishedSpans();
440
- expect(spans[0].attributes['enduser.id']).toBe('user-abc123');
441
- });
442
-
443
- it('should extract endpoint name using getEndpointName', async () => {
444
- const endpointMiddleware = telemetryMiddleware({
445
- getEndpointName: (event) => event.endpointName,
446
- });
447
-
448
- const event = {
449
- httpMethod: 'GET',
450
- path: '/api/users',
451
- headers: {},
452
- endpointName: 'ListUsers',
453
- };
454
-
455
- await endpointMiddleware.before?.({
456
- event,
457
- context: mockContext,
458
- response: null,
459
- } as any);
460
- await endpointMiddleware.after?.({
461
- event,
462
- context: mockContext,
463
- response: { statusCode: 200 },
464
- } as any);
465
-
466
- const spans = exporter.getFinishedSpans();
467
- expect(spans[0].attributes['endpoint.name']).toBe('ListUsers');
468
- });
469
-
470
- it('should extract operation ID using getOperationId', async () => {
471
- const opMiddleware = telemetryMiddleware({
472
- getOperationId: (event) => event.operationId,
473
- });
474
-
475
- const event = {
476
- httpMethod: 'POST',
477
- path: '/api/orders',
478
- headers: {},
479
- operationId: 'createOrder',
480
- };
481
-
482
- await opMiddleware.before?.({
483
- event,
484
- context: mockContext,
485
- response: null,
486
- } as any);
487
- await opMiddleware.after?.({
488
- event,
489
- context: mockContext,
490
- response: { statusCode: 201 },
491
- } as any);
492
-
493
- const spans = exporter.getFinishedSpans();
494
- expect(spans[0].attributes['endpoint.operation_id']).toBe('createOrder');
495
- });
496
- });
497
-
498
- describe('trace context propagation', () => {
499
- it('should extract trace context from traceparent header', async () => {
500
- const traceId = '0af7651916cd43dd8448eb211c80319c';
501
- const parentSpanId = 'b7ad6b7169203331';
502
-
503
- const event = {
504
- httpMethod: 'GET',
505
- path: '/api/downstream',
506
- headers: {
507
- traceparent: `00-${traceId}-${parentSpanId}-01`,
508
- },
509
- };
510
-
511
- await middleware.before?.({
512
- event,
513
- context: mockContext,
514
- response: null,
515
- } as any);
516
- await middleware.after?.({
517
- event,
518
- context: mockContext,
519
- response: { statusCode: 200 },
520
- } as any);
521
-
522
- const spans = exporter.getFinishedSpans();
523
- expect(spans).toHaveLength(1);
524
- // The span should be created (even if not linked to parent in this basic setup)
525
- expect(spans[0].name).toContain('GET');
526
- });
527
- });
528
- });