@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,271 +0,0 @@
1
- /**
2
- * OpenTelemetry implementation of the Telemetry interface
3
- *
4
- * This provides automatic span creation for Lambda handlers using
5
- * OpenTelemetry. Works with any OTel-compatible backend (Jaeger, Zipkin,
6
- * Datadog, Honeycomb, etc.)
7
- */
8
- import { type Span, trace } from '@opentelemetry/api';
9
- import type { Context as LambdaContext } from 'aws-lambda';
10
-
11
- /**
12
- * Context object returned by onRequestStart, passed to subsequent hooks.
13
- * This matches the Telemetry interface from @geekmidas/constructs.
14
- */
15
- export interface TelemetryContext {
16
- [key: string]: unknown;
17
- }
18
-
19
- /**
20
- * Request information passed to telemetry hooks.
21
- * This matches the Telemetry interface from @geekmidas/constructs.
22
- */
23
- export interface TelemetryRequest {
24
- event: any;
25
- context: LambdaContext;
26
- }
27
-
28
- /**
29
- * Response information passed to onRequestEnd.
30
- * This matches the Telemetry interface from @geekmidas/constructs.
31
- */
32
- export interface TelemetryResponse {
33
- statusCode: number;
34
- body?: string;
35
- headers?: Record<string, string>;
36
- }
37
-
38
- /**
39
- * Telemetry interface for instrumenting endpoint requests.
40
- * This matches the Telemetry interface from @geekmidas/constructs.
41
- */
42
- export interface Telemetry {
43
- onRequestStart(request: TelemetryRequest): TelemetryContext;
44
- onRequestEnd(ctx: TelemetryContext, response: TelemetryResponse): void;
45
- onRequestError(ctx: TelemetryContext, error: Error): void;
46
- }
47
-
48
- import {
49
- createHttpServerSpan,
50
- endHttpSpan,
51
- extractTraceContext,
52
- type HttpSpanAttributes,
53
- } from './http';
54
-
55
- /**
56
- * Options for OTelTelemetry
57
- */
58
- export interface OTelTelemetryOptions {
59
- /**
60
- * Whether to record request body as span attribute
61
- * @default false
62
- */
63
- recordBody?: boolean;
64
-
65
- /**
66
- * Whether to record response body as span attribute
67
- * @default false
68
- */
69
- recordResponseBody?: boolean;
70
-
71
- /**
72
- * Custom function to extract user ID from event
73
- */
74
- getUserId?: (event: any) => string | undefined;
75
-
76
- /**
77
- * Custom function to extract endpoint name from event
78
- */
79
- getEndpointName?: (event: any) => string | undefined;
80
-
81
- /**
82
- * Custom function to extract operation ID from event
83
- */
84
- getOperationId?: (event: any) => string | undefined;
85
- }
86
-
87
- /**
88
- * Telemetry context with span reference
89
- */
90
- interface OTelContext extends TelemetryContext {
91
- span: Span;
92
- otelContext: any;
93
- }
94
-
95
- /**
96
- * OpenTelemetry implementation of the Telemetry interface
97
- *
98
- * @example
99
- * ```typescript
100
- * import { OTelTelemetry } from '@geekmidas/telescope/instrumentation';
101
- *
102
- * const adaptor = new AmazonApiGatewayV2Endpoint(envParser, endpoint, {
103
- * telemetry: new OTelTelemetry(),
104
- * });
105
- * ```
106
- *
107
- * @example With options
108
- * ```typescript
109
- * const adaptor = new AmazonApiGatewayV2Endpoint(envParser, endpoint, {
110
- * telemetry: new OTelTelemetry({
111
- * recordBody: true,
112
- * getUserId: (event) => event.requestContext?.authorizer?.userId,
113
- * }),
114
- * });
115
- * ```
116
- */
117
- export class OTelTelemetry implements Telemetry {
118
- constructor(private options: OTelTelemetryOptions = {}) {}
119
-
120
- onRequestStart(request: TelemetryRequest): TelemetryContext {
121
- const { event, context: lambdaContext } = request;
122
-
123
- // Extract trace context from headers
124
- const headers = normalizeHeaders(event.headers || {});
125
- const parentContext = extractTraceContext(headers);
126
-
127
- // Build span attributes from event
128
- const attrs = this.buildSpanAttributes(event, lambdaContext);
129
-
130
- // Create the span
131
- const span = createHttpServerSpan(attrs, parentContext);
132
- const otelContext = trace.setSpan(parentContext, span);
133
-
134
- // Add Lambda-specific attributes
135
- span.setAttribute('faas.invocation_id', lambdaContext.awsRequestId);
136
- span.setAttribute('faas.name', lambdaContext.functionName);
137
-
138
- // Record body if requested
139
- if (this.options.recordBody && event.body) {
140
- const bodyStr =
141
- typeof event.body === 'string'
142
- ? event.body
143
- : JSON.stringify(event.body);
144
- span.setAttribute(
145
- 'http.request.body',
146
- bodyStr.length > 4096 ? `${bodyStr.slice(0, 4096)}...` : bodyStr,
147
- );
148
- }
149
-
150
- return { span, otelContext } as OTelContext;
151
- }
152
-
153
- onRequestEnd(ctx: TelemetryContext, response: TelemetryResponse): void {
154
- const { span } = ctx as OTelContext;
155
-
156
- if (this.options.recordResponseBody && response.body) {
157
- span.setAttribute(
158
- 'http.response.body',
159
- response.body.length > 4096
160
- ? `${response.body.slice(0, 4096)}...`
161
- : response.body,
162
- );
163
- }
164
-
165
- endHttpSpan(span, { statusCode: response.statusCode });
166
- }
167
-
168
- onRequestError(ctx: TelemetryContext, error: Error): void {
169
- const { span } = ctx as OTelContext;
170
- endHttpSpan(span, { statusCode: 500 }, error);
171
- }
172
-
173
- /**
174
- * Build span attributes from Lambda event
175
- */
176
- private buildSpanAttributes(
177
- event: any,
178
- lambdaContext: any,
179
- ): HttpSpanAttributes {
180
- // Detect event type (API Gateway v1, v2, ALB, or direct invoke)
181
- const isV2 =
182
- 'requestContext' in event && 'http' in (event.requestContext || {});
183
- const isV1 =
184
- 'requestContext' in event &&
185
- 'httpMethod' in event &&
186
- !('http' in (event.requestContext || {}));
187
-
188
- let method = 'INVOKE';
189
- let path = '/';
190
- let route: string | undefined;
191
- let host: string | undefined;
192
- let scheme: 'http' | 'https' | undefined;
193
- let userAgent: string | undefined;
194
- let clientIp: string | undefined;
195
- let requestId: string | undefined;
196
-
197
- if (isV2) {
198
- // API Gateway v2 (HTTP API)
199
- const http = event.requestContext.http;
200
- method = http.method;
201
- path = event.rawPath || http.path;
202
- route = event.routeKey?.replace(/^[A-Z]+\s+/, ''); // Remove HTTP method prefix
203
- host = event.headers?.host;
204
- scheme = 'https';
205
- userAgent = http.userAgent;
206
- clientIp = http.sourceIp;
207
- requestId = event.requestContext.requestId;
208
- } else if (isV1) {
209
- // API Gateway v1 (REST API)
210
- method = event.httpMethod;
211
- path = event.path;
212
- route = event.resource;
213
- host = event.headers?.Host || event.headers?.host;
214
- scheme = 'https';
215
- userAgent =
216
- event.headers?.['User-Agent'] || event.headers?.['user-agent'];
217
- clientIp = event.requestContext?.identity?.sourceIp;
218
- requestId = event.requestContext?.requestId;
219
- } else if ('httpMethod' in event) {
220
- // ALB event
221
- method = event.httpMethod;
222
- path = event.path;
223
- host = event.headers?.host;
224
- userAgent =
225
- event.headers?.['user-agent'] || event.headers?.['User-Agent'];
226
- clientIp = event.headers?.['x-forwarded-for']?.split(',')[0]?.trim();
227
- }
228
-
229
- const attrs: HttpSpanAttributes = {
230
- method,
231
- path,
232
- route,
233
- host,
234
- scheme,
235
- userAgent,
236
- clientIp,
237
- requestId: requestId || lambdaContext.awsRequestId,
238
- };
239
-
240
- // Add endpoint metadata if extractors provided
241
- if (this.options.getEndpointName || this.options.getOperationId) {
242
- attrs.endpoint = {
243
- name: this.options.getEndpointName?.(event),
244
- operationId: this.options.getOperationId?.(event),
245
- };
246
- }
247
-
248
- // Add user metadata if extractor provided
249
- if (this.options.getUserId) {
250
- const userId = this.options.getUserId(event);
251
- if (userId) {
252
- attrs.user = { userId };
253
- }
254
- }
255
-
256
- return attrs;
257
- }
258
- }
259
-
260
- /**
261
- * Normalize headers to lowercase keys
262
- */
263
- function normalizeHeaders(
264
- headers: Record<string, string | string[] | undefined>,
265
- ): Record<string, string | string[] | undefined> {
266
- const normalized: Record<string, string | string[] | undefined> = {};
267
- for (const [key, value] of Object.entries(headers)) {
268
- normalized[key.toLowerCase()] = value;
269
- }
270
- return normalized;
271
- }
@@ -1,246 +0,0 @@
1
- import { DiagConsoleLogger, DiagLogLevel, diag } from '@opentelemetry/api';
2
- import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
3
- import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';
4
- import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
5
- import { PinoInstrumentation } from '@opentelemetry/instrumentation-pino';
6
- import { Resource } from '@opentelemetry/resources';
7
- import {
8
- BatchLogRecordProcessor,
9
- LoggerProvider,
10
- } from '@opentelemetry/sdk-logs';
11
- import { NodeSDK } from '@opentelemetry/sdk-node';
12
- import {
13
- ConsoleSpanExporter,
14
- type SpanExporter,
15
- } from '@opentelemetry/sdk-trace-node';
16
- import {
17
- ATTR_SERVICE_NAME,
18
- ATTR_SERVICE_VERSION,
19
- } from '@opentelemetry/semantic-conventions';
20
- import type { SpanProcessorStrategy } from '../adapters/types';
21
- import {
22
- createSpanProcessor,
23
- getRecommendedStrategy,
24
- setGlobalLogProcessor,
25
- setGlobalSpanProcessor,
26
- } from './core';
27
-
28
- /**
29
- * Options for configuring telemetry
30
- */
31
- export interface TelemetryOptions {
32
- /**
33
- * Service name for resource identification
34
- */
35
- serviceName: string;
36
-
37
- /**
38
- * Service version
39
- */
40
- serviceVersion?: string;
41
-
42
- /**
43
- * OTLP endpoint URL (e.g., 'http://localhost:3000/__telescope/v1')
44
- * If not provided, traces will be logged to console
45
- */
46
- endpoint?: string;
47
-
48
- /**
49
- * Whether to instrument Pino for log correlation
50
- * @default true
51
- */
52
- instrumentPino?: boolean;
53
-
54
- /**
55
- * Whether to enable auto-instrumentation for common libraries
56
- * (http, fetch, express, etc.)
57
- * @default true
58
- */
59
- autoInstrument?: boolean;
60
-
61
- /**
62
- * Enable debug logging for OTel SDK
63
- * @default false
64
- */
65
- debug?: boolean;
66
-
67
- /**
68
- * Additional resource attributes
69
- */
70
- resourceAttributes?: Record<string, string>;
71
-
72
- /**
73
- * Headers to send with OTLP requests
74
- */
75
- headers?: Record<string, string>;
76
-
77
- /**
78
- * Span processor strategy.
79
- * - 'batch': Efficient batching for long-running servers (default)
80
- * - 'simple': Immediate export for serverless environments (Lambda, Edge)
81
- *
82
- * If not specified, automatically selected based on environment detection.
83
- */
84
- spanProcessorStrategy?: SpanProcessorStrategy;
85
-
86
- /**
87
- * Environment type for automatic configuration
88
- * @default 'server'
89
- */
90
- environment?: 'server' | 'lambda' | 'edge' | 'custom';
91
- }
92
-
93
- let sdk: NodeSDK | null = null;
94
-
95
- /**
96
- * Set up OpenTelemetry instrumentation.
97
- * Call this BEFORE importing your application code.
98
- *
99
- * @example
100
- * ```typescript
101
- * // instrumentation.ts (create this file)
102
- * import { setupTelemetry } from '@geekmidas/telescope/instrumentation';
103
- *
104
- * setupTelemetry({
105
- * serviceName: 'my-api',
106
- * endpoint: 'http://localhost:3000/__telescope/v1',
107
- * });
108
- *
109
- * // Then in your entry point:
110
- * // import './instrumentation';
111
- * // import { app } from './app';
112
- * ```
113
- */
114
- export function setupTelemetry(options: TelemetryOptions): void {
115
- if (sdk) {
116
- return;
117
- }
118
-
119
- const {
120
- serviceName,
121
- serviceVersion = '1.0.0',
122
- endpoint,
123
- instrumentPino = true,
124
- autoInstrument = true,
125
- debug = false,
126
- resourceAttributes = {},
127
- headers = {},
128
- spanProcessorStrategy,
129
- environment = 'server',
130
- } = options;
131
-
132
- // Enable debug logging if requested
133
- if (debug) {
134
- diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.DEBUG);
135
- }
136
-
137
- // Create resource
138
- const resource = new Resource({
139
- [ATTR_SERVICE_NAME]: serviceName,
140
- [ATTR_SERVICE_VERSION]: serviceVersion,
141
- ...resourceAttributes,
142
- });
143
-
144
- // Build instrumentations list
145
- const instrumentations = [];
146
-
147
- // Add Pino instrumentation for log correlation
148
- if (instrumentPino) {
149
- instrumentations.push(
150
- new PinoInstrumentation({
151
- // Inject trace context into log records
152
- logHook: (span, record) => {
153
- record.trace_id = span.spanContext().traceId;
154
- record.span_id = span.spanContext().spanId;
155
- record.trace_flags = span.spanContext().traceFlags;
156
- },
157
- }),
158
- );
159
- }
160
-
161
- // Add auto-instrumentations for common libraries
162
- if (autoInstrument) {
163
- instrumentations.push(
164
- getNodeAutoInstrumentations({
165
- // Disable file system instrumentation (too noisy)
166
- '@opentelemetry/instrumentation-fs': { enabled: false },
167
- // Configure HTTP instrumentation
168
- '@opentelemetry/instrumentation-http': {
169
- ignoreIncomingRequestHook: (request) => {
170
- // Ignore health checks and internal routes
171
- const path = request.url || '';
172
- return (
173
- path.includes('/__health') ||
174
- path.includes('/__telescope') ||
175
- path.includes('/favicon')
176
- );
177
- },
178
- },
179
- }),
180
- );
181
- }
182
-
183
- // Create exporters
184
- let traceExporter: SpanExporter;
185
- if (endpoint) {
186
- traceExporter = new OTLPTraceExporter({
187
- url: `${endpoint}/traces`,
188
- headers,
189
- });
190
- } else {
191
- // Fall back to console exporter for debugging
192
- traceExporter = new ConsoleSpanExporter();
193
- }
194
-
195
- // Determine span processor strategy
196
- const strategy = spanProcessorStrategy ?? getRecommendedStrategy(environment);
197
- const spanProcessor = createSpanProcessor(traceExporter, { strategy });
198
-
199
- // Register globally for flush operations
200
- setGlobalSpanProcessor(spanProcessor);
201
-
202
- // Create and configure SDK
203
- sdk = new NodeSDK({
204
- resource,
205
- traceExporter,
206
- spanProcessors: [spanProcessor],
207
- instrumentations,
208
- });
209
-
210
- // Set up log exporter if endpoint is provided
211
- if (endpoint) {
212
- const logExporter = new OTLPLogExporter({
213
- url: `${endpoint}/logs`,
214
- headers,
215
- });
216
-
217
- const loggerProvider = new LoggerProvider({ resource });
218
- const logProcessor = new BatchLogRecordProcessor(logExporter);
219
- loggerProvider.addLogRecordProcessor(logProcessor);
220
-
221
- // Register for flush operations
222
- setGlobalLogProcessor(logProcessor);
223
- }
224
-
225
- // Start the SDK
226
- sdk.start();
227
-
228
- // Graceful shutdown
229
- process.on('SIGTERM', () => {
230
- sdk
231
- ?.shutdown()
232
- .then(() => {})
233
- .catch((_error) => {})
234
- .finally(() => process.exit(0));
235
- });
236
- }
237
-
238
- /**
239
- * Shut down telemetry (for testing or graceful shutdown)
240
- */
241
- export async function shutdownTelemetry(): Promise<void> {
242
- if (sdk) {
243
- await sdk.shutdown();
244
- sdk = null;
245
- }
246
- }
@@ -1,145 +0,0 @@
1
- import {
2
- type Attributes,
3
- context,
4
- type Span,
5
- SpanStatusCode,
6
- trace,
7
- } from '@opentelemetry/api';
8
-
9
- /**
10
- * Get the currently active span
11
- */
12
- export function getActiveSpan(): Span | undefined {
13
- return trace.getActiveSpan();
14
- }
15
-
16
- /**
17
- * Set attributes on the active span
18
- */
19
- export function setSpanAttributes(attributes: Attributes): void {
20
- const span = getActiveSpan();
21
- if (span) {
22
- span.setAttributes(attributes);
23
- }
24
- }
25
-
26
- /**
27
- * Create a new span as a child of the current span
28
- */
29
- export function createSpan(name: string, attributes?: Attributes): Span {
30
- const tracer = trace.getTracer('@geekmidas/telescope');
31
- const span = tracer.startSpan(name, { attributes });
32
- return span;
33
- }
34
-
35
- /**
36
- * Execute a function within a new span context
37
- *
38
- * @example
39
- * ```typescript
40
- * const result = await withSpan('processOrder', async (span) => {
41
- * span.setAttribute('order.id', orderId);
42
- * const order = await processOrder(orderId);
43
- * return order;
44
- * });
45
- * ```
46
- */
47
- export async function withSpan<T>(
48
- name: string,
49
- fn: (span: Span) => Promise<T>,
50
- attributes?: Attributes,
51
- ): Promise<T> {
52
- const tracer = trace.getTracer('@geekmidas/telescope');
53
- const span = tracer.startSpan(name, { attributes });
54
-
55
- try {
56
- const result = await context.with(
57
- trace.setSpan(context.active(), span),
58
- () => fn(span),
59
- );
60
- span.setStatus({ code: SpanStatusCode.OK });
61
- return result;
62
- } catch (error) {
63
- span.setStatus({
64
- code: SpanStatusCode.ERROR,
65
- message: error instanceof Error ? error.message : 'Unknown error',
66
- });
67
- span.recordException(
68
- error instanceof Error ? error : new Error(String(error)),
69
- );
70
- throw error;
71
- } finally {
72
- span.end();
73
- }
74
- }
75
-
76
- /**
77
- * Synchronous version of withSpan
78
- */
79
- export function withSpanSync<T>(
80
- name: string,
81
- fn: (span: Span) => T,
82
- attributes?: Attributes,
83
- ): T {
84
- const tracer = trace.getTracer('@geekmidas/telescope');
85
- const span = tracer.startSpan(name, { attributes });
86
-
87
- try {
88
- const result = context.with(trace.setSpan(context.active(), span), () =>
89
- fn(span),
90
- );
91
- span.setStatus({ code: SpanStatusCode.OK });
92
- return result;
93
- } catch (error) {
94
- span.setStatus({
95
- code: SpanStatusCode.ERROR,
96
- message: error instanceof Error ? error.message : 'Unknown error',
97
- });
98
- span.recordException(
99
- error instanceof Error ? error : new Error(String(error)),
100
- );
101
- throw error;
102
- } finally {
103
- span.end();
104
- }
105
- }
106
-
107
- /**
108
- * Get the current trace ID (useful for log correlation)
109
- */
110
- export function getTraceId(): string | undefined {
111
- const span = getActiveSpan();
112
- return span?.spanContext().traceId;
113
- }
114
-
115
- /**
116
- * Get the current span ID
117
- */
118
- export function getSpanId(): string | undefined {
119
- const span = getActiveSpan();
120
- return span?.spanContext().spanId;
121
- }
122
-
123
- /**
124
- * Record an exception on the current span
125
- */
126
- export function recordException(error: Error): void {
127
- const span = getActiveSpan();
128
- if (span) {
129
- span.recordException(error);
130
- span.setStatus({
131
- code: SpanStatusCode.ERROR,
132
- message: error.message,
133
- });
134
- }
135
- }
136
-
137
- /**
138
- * Add an event to the current span
139
- */
140
- export function addSpanEvent(name: string, attributes?: Attributes): void {
141
- const span = getActiveSpan();
142
- if (span) {
143
- span.addEvent(name, attributes);
144
- }
145
- }