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