@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,306 +0,0 @@
1
- import {
2
- type Attributes,
3
- type Context,
4
- context,
5
- propagation,
6
- type Span,
7
- SpanKind,
8
- SpanStatusCode,
9
- trace,
10
- } from '@opentelemetry/api';
11
-
12
- /**
13
- * HTTP request attributes following OpenTelemetry semantic conventions
14
- */
15
- export interface HttpRequestAttributes {
16
- method: string;
17
- url?: string;
18
- path?: string;
19
- route?: string;
20
- host?: string;
21
- scheme?: 'http' | 'https';
22
- userAgent?: string;
23
- clientIp?: string;
24
- requestId?: string;
25
- }
26
-
27
- /**
28
- * HTTP response attributes
29
- */
30
- export interface HttpResponseAttributes {
31
- statusCode: number;
32
- responseSize?: number;
33
- }
34
-
35
- /**
36
- * Endpoint-specific attributes
37
- */
38
- export interface EndpointAttributes {
39
- name?: string;
40
- operationId?: string;
41
- tags?: string[];
42
- }
43
-
44
- /**
45
- * User/session attributes
46
- */
47
- export interface UserAttributes {
48
- userId?: string;
49
- sessionId?: string;
50
- roles?: string[];
51
- }
52
-
53
- /**
54
- * Full span attributes for HTTP requests
55
- */
56
- export interface HttpSpanAttributes extends HttpRequestAttributes {
57
- response?: HttpResponseAttributes;
58
- endpoint?: EndpointAttributes;
59
- user?: UserAttributes;
60
- custom?: Attributes;
61
- }
62
-
63
- const TRACER_NAME = '@geekmidas/constructs';
64
-
65
- /**
66
- * Get or create a tracer for constructs instrumentation
67
- */
68
- export function getConstructsTracer() {
69
- return trace.getTracer(TRACER_NAME);
70
- }
71
-
72
- /**
73
- * Convert HTTP attributes to OpenTelemetry semantic conventions
74
- */
75
- export function toOtelAttributes(attrs: HttpSpanAttributes): Attributes {
76
- const otelAttrs: Attributes = {};
77
-
78
- // HTTP request attributes (OpenTelemetry semantic conventions)
79
- if (attrs.method) otelAttrs['http.request.method'] = attrs.method;
80
- if (attrs.url) otelAttrs['url.full'] = attrs.url;
81
- if (attrs.path) otelAttrs['url.path'] = attrs.path;
82
- if (attrs.route) otelAttrs['http.route'] = attrs.route;
83
- if (attrs.host) otelAttrs['server.address'] = attrs.host;
84
- if (attrs.scheme) otelAttrs['url.scheme'] = attrs.scheme;
85
- if (attrs.userAgent) otelAttrs['user_agent.original'] = attrs.userAgent;
86
- if (attrs.clientIp) otelAttrs['client.address'] = attrs.clientIp;
87
- if (attrs.requestId) otelAttrs['http.request.id'] = attrs.requestId;
88
-
89
- // HTTP response attributes
90
- if (attrs.response?.statusCode) {
91
- otelAttrs['http.response.status_code'] = attrs.response.statusCode;
92
- }
93
- if (attrs.response?.responseSize) {
94
- otelAttrs['http.response.body.size'] = attrs.response.responseSize;
95
- }
96
-
97
- // Endpoint attributes
98
- if (attrs.endpoint?.name) otelAttrs['endpoint.name'] = attrs.endpoint.name;
99
- if (attrs.endpoint?.operationId) {
100
- otelAttrs['endpoint.operation_id'] = attrs.endpoint.operationId;
101
- }
102
- if (attrs.endpoint?.tags?.length) {
103
- otelAttrs['endpoint.tags'] = attrs.endpoint.tags;
104
- }
105
-
106
- // User attributes
107
- if (attrs.user?.userId) otelAttrs['enduser.id'] = attrs.user.userId;
108
- if (attrs.user?.sessionId) otelAttrs['session.id'] = attrs.user.sessionId;
109
- if (attrs.user?.roles?.length)
110
- otelAttrs['enduser.role'] = attrs.user.roles.join(',');
111
-
112
- // Custom attributes
113
- if (attrs.custom) {
114
- Object.assign(otelAttrs, attrs.custom);
115
- }
116
-
117
- return otelAttrs;
118
- }
119
-
120
- /**
121
- * Extract trace context from HTTP headers
122
- */
123
- export function extractTraceContext(
124
- headers: Record<string, string | string[] | undefined>,
125
- ): Context {
126
- // Normalize headers to Record<string, string>
127
- const normalizedHeaders: Record<string, string> = {};
128
- for (const [key, value] of Object.entries(headers)) {
129
- if (value) {
130
- const stringValue = Array.isArray(value) ? value[0] : value;
131
- if (stringValue) {
132
- normalizedHeaders[key.toLowerCase()] = stringValue;
133
- }
134
- }
135
- }
136
-
137
- return propagation.extract(context.active(), normalizedHeaders);
138
- }
139
-
140
- /**
141
- * Inject trace context into HTTP headers
142
- */
143
- export function injectTraceContext(
144
- headers: Record<string, string>,
145
- ctx?: Context,
146
- ): Record<string, string> {
147
- const result = { ...headers };
148
- propagation.inject(ctx ?? context.active(), result);
149
- return result;
150
- }
151
-
152
- /**
153
- * Create an HTTP server span for an incoming request
154
- */
155
- export function createHttpServerSpan(
156
- attrs: HttpSpanAttributes,
157
- parentContext?: Context,
158
- ): Span {
159
- const tracer = getConstructsTracer();
160
- const spanName = attrs.route
161
- ? `${attrs.method} ${attrs.route}`
162
- : `${attrs.method} ${attrs.path || '/'}`;
163
-
164
- const ctx = parentContext ?? context.active();
165
-
166
- return tracer.startSpan(
167
- spanName,
168
- {
169
- kind: SpanKind.SERVER,
170
- attributes: toOtelAttributes(attrs),
171
- },
172
- ctx,
173
- );
174
- }
175
-
176
- /**
177
- * End an HTTP span with response data
178
- */
179
- export function endHttpSpan(
180
- span: Span,
181
- response: HttpResponseAttributes,
182
- error?: Error,
183
- ): void {
184
- span.setAttribute('http.response.status_code', response.statusCode);
185
-
186
- if (response.responseSize) {
187
- span.setAttribute('http.response.body.size', response.responseSize);
188
- }
189
-
190
- if (error) {
191
- span.recordException(error);
192
- span.setStatus({
193
- code: SpanStatusCode.ERROR,
194
- message: error.message,
195
- });
196
- } else if (response.statusCode >= 400) {
197
- span.setStatus({
198
- code: SpanStatusCode.ERROR,
199
- message: `HTTP ${response.statusCode}`,
200
- });
201
- } else {
202
- span.setStatus({ code: SpanStatusCode.OK });
203
- }
204
-
205
- span.end();
206
- }
207
-
208
- /**
209
- * Execute a function within an HTTP server span context
210
- *
211
- * @example
212
- * ```typescript
213
- * const result = await withHttpSpan(
214
- * { method: 'GET', path: '/users', route: '/users' },
215
- * async (span) => {
216
- * const users = await getUsers();
217
- * return { statusCode: 200, body: users };
218
- * }
219
- * );
220
- * ```
221
- */
222
- export async function withHttpSpan<T>(
223
- attrs: HttpSpanAttributes,
224
- fn: (span: Span) => Promise<T & { statusCode?: number }>,
225
- parentContext?: Context,
226
- ): Promise<T> {
227
- const span = createHttpServerSpan(attrs, parentContext);
228
-
229
- try {
230
- const result = await context.with(
231
- trace.setSpan(parentContext ?? context.active(), span),
232
- () => fn(span),
233
- );
234
-
235
- const statusCode = (result as { statusCode?: number }).statusCode ?? 200;
236
- endHttpSpan(span, { statusCode });
237
-
238
- return result;
239
- } catch (error) {
240
- endHttpSpan(
241
- span,
242
- { statusCode: 500 },
243
- error instanceof Error ? error : new Error(String(error)),
244
- );
245
- throw error;
246
- }
247
- }
248
-
249
- /**
250
- * Create a child span for internal operations (middleware, handlers, etc.)
251
- */
252
- export function createChildSpan(name: string, attributes?: Attributes): Span {
253
- const tracer = getConstructsTracer();
254
- return tracer.startSpan(name, {
255
- kind: SpanKind.INTERNAL,
256
- attributes,
257
- });
258
- }
259
-
260
- /**
261
- * Execute a function within a child span
262
- */
263
- export async function withChildSpan<T>(
264
- name: string,
265
- fn: (span: Span) => Promise<T>,
266
- attributes?: Attributes,
267
- ): Promise<T> {
268
- const span = createChildSpan(name, attributes);
269
-
270
- try {
271
- const result = await context.with(
272
- trace.setSpan(context.active(), span),
273
- () => fn(span),
274
- );
275
- span.setStatus({ code: SpanStatusCode.OK });
276
- return result;
277
- } catch (error) {
278
- span.setStatus({
279
- code: SpanStatusCode.ERROR,
280
- message: error instanceof Error ? error.message : 'Unknown error',
281
- });
282
- span.recordException(
283
- error instanceof Error ? error : new Error(String(error)),
284
- );
285
- throw error;
286
- } finally {
287
- span.end();
288
- }
289
- }
290
-
291
- /**
292
- * Check if tracing is enabled (OpenTelemetry API is available and configured)
293
- */
294
- export function isTracingEnabled(): boolean {
295
- try {
296
- const tracer = trace.getTracer(TRACER_NAME);
297
- // Check if we have a real tracer (not a no-op)
298
- const span = tracer.startSpan('test');
299
- const isReal =
300
- span.spanContext().traceId !== '00000000000000000000000000000000';
301
- span.end();
302
- return isReal;
303
- } catch {
304
- return false;
305
- }
306
- }
@@ -1,90 +0,0 @@
1
- /**
2
- * OpenTelemetry Instrumentation for @geekmidas/constructs
3
- *
4
- * This module provides automatic instrumentation for constructs endpoints,
5
- * functions, crons, and subscribers using the OpenTelemetry SDK.
6
- *
7
- * @example
8
- * ```typescript
9
- * import { setupTelemetry } from '@geekmidas/telescope/instrumentation';
10
- *
11
- * // Call before importing your app code
12
- * setupTelemetry({
13
- * serviceName: 'my-api',
14
- * // Dev: send to Telescope
15
- * endpoint: 'http://localhost:3000/__telescope/v1',
16
- * // Enable Pino log correlation
17
- * instrumentPino: true,
18
- * });
19
- * ```
20
- */
21
-
22
- export type {
23
- SpanProcessorOptions,
24
- SpanProcessorStrategy,
25
- } from '../adapters/types';
26
- export {
27
- createSpanProcessor,
28
- enableTelemetryDebug,
29
- flushTelemetry,
30
- getRecommendedStrategy,
31
- isTelemetryInitialized,
32
- NoopSpanProcessor,
33
- setGlobalLogProcessor,
34
- setGlobalSpanProcessor,
35
- withTelemetryFlush,
36
- } from './core';
37
- export type { HonoTelemetryMiddlewareOptions } from './hono';
38
- // Middleware for Hono auto-instrumentation
39
- export {
40
- getSpanFromContext,
41
- getTraceContextFromHono,
42
- honoTelemetryMiddleware,
43
- withHonoSpanContext,
44
- } from './hono';
45
- export type {
46
- EndpointAttributes,
47
- HttpRequestAttributes,
48
- HttpResponseAttributes,
49
- HttpSpanAttributes,
50
- UserAttributes,
51
- } from './http';
52
-
53
- // HTTP instrumentation utilities for constructs
54
- export {
55
- createChildSpan,
56
- createHttpServerSpan,
57
- endHttpSpan,
58
- extractTraceContext,
59
- getConstructsTracer,
60
- injectTraceContext,
61
- isTracingEnabled,
62
- toOtelAttributes,
63
- withChildSpan,
64
- withHttpSpan,
65
- } from './http';
66
- export type { TelemetryMiddlewareOptions } from './middleware';
67
-
68
- // Middleware for Lambda auto-instrumentation
69
- export {
70
- getContextFromEvent,
71
- getSpanFromEvent,
72
- telemetryMiddleware,
73
- withEventContext,
74
- } from './middleware';
75
- export type { OTelTelemetryOptions } from './otel';
76
- // OTelTelemetry - Telemetry interface implementation using OpenTelemetry
77
- export { OTelTelemetry } from './otel';
78
- export type { TelemetryOptions } from './setup';
79
- export { setupTelemetry, shutdownTelemetry } from './setup';
80
- export {
81
- addSpanEvent,
82
- createSpan,
83
- getActiveSpan,
84
- getSpanId,
85
- getTraceId,
86
- recordException,
87
- setSpanAttributes,
88
- withSpan,
89
- withSpanSync,
90
- } from './tracing';
@@ -1,297 +0,0 @@
1
- /**
2
- * OpenTelemetry instrumentation middleware for @geekmidas/constructs
3
- *
4
- * This middleware automatically creates spans for HTTP requests,
5
- * extracts trace context from incoming headers, and records
6
- * request/response metadata as span attributes.
7
- */
8
- import type { MiddlewareObj } from '@middy/core';
9
- import { context, type Span, trace } from '@opentelemetry/api';
10
- import type { Context as LambdaContext } from 'aws-lambda';
11
- import {
12
- createHttpServerSpan,
13
- endHttpSpan,
14
- extractTraceContext,
15
- type HttpSpanAttributes,
16
- } from './http';
17
-
18
- /**
19
- * Options for the telemetry middleware
20
- */
21
- export interface TelemetryMiddlewareOptions {
22
- /**
23
- * Whether to record request body as span attribute
24
- * @default false
25
- */
26
- recordBody?: boolean;
27
-
28
- /**
29
- * Whether to record response body as span attribute
30
- * @default false
31
- */
32
- recordResponseBody?: boolean;
33
-
34
- /**
35
- * Custom function to extract endpoint name from event
36
- */
37
- getEndpointName?: (event: any) => string | undefined;
38
-
39
- /**
40
- * Custom function to extract operation ID from event
41
- */
42
- getOperationId?: (event: any) => string | undefined;
43
-
44
- /**
45
- * Custom function to extract user ID from event
46
- */
47
- getUserId?: (event: any) => string | undefined;
48
-
49
- /**
50
- * Whether to skip tracing for this request
51
- */
52
- shouldSkip?: (event: any) => boolean;
53
- }
54
-
55
- // Symbol for storing span on event
56
- const SPAN_SYMBOL = Symbol('telemetry.span');
57
- const CONTEXT_SYMBOL = Symbol('telemetry.context');
58
-
59
- /**
60
- * Get the current span from the Lambda event
61
- */
62
- export function getSpanFromEvent(event: any): Span | undefined {
63
- return event?.[SPAN_SYMBOL];
64
- }
65
-
66
- /**
67
- * Get the current trace context from the Lambda event
68
- */
69
- export function getContextFromEvent(event: any): any {
70
- return event?.[CONTEXT_SYMBOL];
71
- }
72
-
73
- /**
74
- * Create a telemetry middleware for Lambda handlers
75
- *
76
- * @example
77
- * ```typescript
78
- * import { telemetryMiddleware } from '@geekmidas/telescope/instrumentation';
79
- *
80
- * const adaptor = new AmazonApiGatewayV2Endpoint(envParser, endpoint, {
81
- * telemetry: { middleware: telemetryMiddleware() },
82
- * });
83
- * ```
84
- */
85
- export function telemetryMiddleware(
86
- options: TelemetryMiddlewareOptions = {},
87
- ): MiddlewareObj<any, any, Error, LambdaContext> {
88
- return {
89
- before: async (request) => {
90
- // Skip if custom skip function returns true
91
- if (options.shouldSkip?.(request.event)) {
92
- return;
93
- }
94
-
95
- const event = request.event;
96
- const lambdaContext = request.context;
97
-
98
- // Extract trace context from headers
99
- const headers = normalizeHeaders(event.headers || {});
100
- const parentContext = extractTraceContext(headers);
101
-
102
- // Build span attributes from event
103
- const attrs = buildSpanAttributes(event, lambdaContext, options);
104
-
105
- // Create the span
106
- const span = createHttpServerSpan(attrs, parentContext);
107
-
108
- // Store span and context on event for access in handler
109
- event[SPAN_SYMBOL] = span;
110
- event[CONTEXT_SYMBOL] = trace.setSpan(parentContext, span);
111
-
112
- // Add Lambda-specific attributes
113
- span.setAttribute('faas.invocation_id', lambdaContext.awsRequestId);
114
- span.setAttribute('faas.name', lambdaContext.functionName);
115
-
116
- if (options.recordBody && event.body) {
117
- const bodyStr =
118
- typeof event.body === 'string'
119
- ? event.body
120
- : JSON.stringify(event.body);
121
- // Truncate large bodies
122
- span.setAttribute(
123
- 'http.request.body',
124
- bodyStr.length > 4096 ? `${bodyStr.slice(0, 4096)}...` : bodyStr,
125
- );
126
- }
127
- },
128
-
129
- after: async (request) => {
130
- const span = getSpanFromEvent(request.event);
131
- if (!span) {
132
- return;
133
- }
134
-
135
- const response = request.response;
136
- const statusCode =
137
- typeof response?.statusCode === 'number' ? response.statusCode : 200;
138
-
139
- if (options.recordResponseBody && response?.body) {
140
- const bodyStr =
141
- typeof response.body === 'string'
142
- ? response.body
143
- : JSON.stringify(response.body);
144
- span.setAttribute(
145
- 'http.response.body',
146
- bodyStr.length > 4096 ? `${bodyStr.slice(0, 4096)}...` : bodyStr,
147
- );
148
- }
149
-
150
- endHttpSpan(span, { statusCode });
151
- },
152
-
153
- onError: async (request) => {
154
- const span = getSpanFromEvent(request.event);
155
- if (!span) {
156
- return;
157
- }
158
-
159
- const error = request.error;
160
- const response = request.response;
161
-
162
- // Determine status code from response or error
163
- let statusCode = 500;
164
- if (response?.statusCode) {
165
- statusCode = response.statusCode;
166
- } else if (error && 'statusCode' in error) {
167
- statusCode = (error as any).statusCode;
168
- }
169
-
170
- endHttpSpan(
171
- span,
172
- { statusCode },
173
- error instanceof Error ? error : new Error(String(error)),
174
- );
175
- },
176
- };
177
- }
178
-
179
- /**
180
- * Build span attributes from Lambda event
181
- */
182
- function buildSpanAttributes(
183
- event: any,
184
- lambdaContext: LambdaContext,
185
- options: TelemetryMiddlewareOptions,
186
- ): HttpSpanAttributes {
187
- // Detect event type (API Gateway v1, v2, ALB, or direct invoke)
188
- const isV2 =
189
- 'requestContext' in event && 'http' in (event.requestContext || {});
190
- const isV1 =
191
- 'requestContext' in event &&
192
- 'httpMethod' in event &&
193
- !('http' in (event.requestContext || {}));
194
-
195
- let method = 'INVOKE';
196
- let path = '/';
197
- let route: string | undefined;
198
- let host: string | undefined;
199
- let scheme: 'http' | 'https' | undefined;
200
- let userAgent: string | undefined;
201
- let clientIp: string | undefined;
202
- let requestId: string | undefined;
203
-
204
- if (isV2) {
205
- // API Gateway v2 (HTTP API)
206
- const http = event.requestContext.http;
207
- method = http.method;
208
- path = event.rawPath || http.path;
209
- route = event.routeKey?.replace(/^[A-Z]+\s+/, ''); // Remove HTTP method prefix
210
- host = event.headers?.host;
211
- scheme = 'https';
212
- userAgent = http.userAgent;
213
- clientIp = http.sourceIp;
214
- requestId = event.requestContext.requestId;
215
- } else if (isV1) {
216
- // API Gateway v1 (REST API)
217
- method = event.httpMethod;
218
- path = event.path;
219
- route = event.resource;
220
- host = event.headers?.Host || event.headers?.host;
221
- scheme = 'https';
222
- userAgent = event.headers?.['User-Agent'] || event.headers?.['user-agent'];
223
- clientIp = event.requestContext?.identity?.sourceIp;
224
- requestId = event.requestContext?.requestId;
225
- } else if ('httpMethod' in event) {
226
- // ALB event
227
- method = event.httpMethod;
228
- path = event.path;
229
- host = event.headers?.host;
230
- userAgent = event.headers?.['user-agent'] || event.headers?.['User-Agent'];
231
- clientIp = event.headers?.['x-forwarded-for']?.split(',')[0]?.trim();
232
- }
233
-
234
- const attrs: HttpSpanAttributes = {
235
- method,
236
- path,
237
- route,
238
- host,
239
- scheme,
240
- userAgent,
241
- clientIp,
242
- requestId: requestId || lambdaContext.awsRequestId,
243
- };
244
-
245
- // Add endpoint metadata if extractors provided
246
- if (options.getEndpointName || options.getOperationId) {
247
- attrs.endpoint = {
248
- name: options.getEndpointName?.(event),
249
- operationId: options.getOperationId?.(event),
250
- };
251
- }
252
-
253
- // Add user metadata if extractor provided
254
- if (options.getUserId) {
255
- const userId = options.getUserId(event);
256
- if (userId) {
257
- attrs.user = { userId };
258
- }
259
- }
260
-
261
- return attrs;
262
- }
263
-
264
- /**
265
- * Normalize headers to lowercase keys
266
- */
267
- function normalizeHeaders(
268
- headers: Record<string, string | string[] | undefined>,
269
- ): Record<string, string | string[] | undefined> {
270
- const normalized: Record<string, string | string[] | undefined> = {};
271
- for (const [key, value] of Object.entries(headers)) {
272
- normalized[key.toLowerCase()] = value;
273
- }
274
- return normalized;
275
- }
276
-
277
- /**
278
- * Run a function within the span context from a Lambda event
279
- *
280
- * @example
281
- * ```typescript
282
- * await withEventContext(event, async () => {
283
- * // Any spans created here will be children of the request span
284
- * await doSomething();
285
- * });
286
- * ```
287
- */
288
- export async function withEventContext<T>(
289
- event: any,
290
- fn: () => Promise<T>,
291
- ): Promise<T> {
292
- const ctx = getContextFromEvent(event);
293
- if (!ctx) {
294
- return fn();
295
- }
296
- return context.with(ctx, fn);
297
- }