@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,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
|
-
});
|