@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.
- package/README.md +14 -4
- package/dist/{http-D5kn3_Mt.d.mts → http-BiNtg85L.d.mts} +3 -3
- package/dist/{http-D5kn3_Mt.d.mts.map → http-BiNtg85L.d.mts.map} +1 -1
- package/dist/instrumentation/http.d.mts +1 -1
- package/dist/instrumentation/index.cjs +2 -2
- package/dist/instrumentation/index.d.cts +2 -2
- package/dist/instrumentation/index.d.mts +3 -3
- package/dist/instrumentation/index.mjs +2 -2
- package/dist/instrumentation/middleware.cjs +1 -1
- package/dist/instrumentation/middleware.d.cts +1 -1
- package/dist/instrumentation/middleware.d.mts +1 -1
- package/dist/instrumentation/middleware.mjs +1 -1
- package/dist/instrumentation/otel.cjs +1 -1
- package/dist/instrumentation/otel.d.cts +1 -1
- package/dist/instrumentation/otel.d.mts +1 -1
- package/dist/instrumentation/otel.mjs +1 -1
- package/dist/logger/console.d.cts.map +1 -1
- package/dist/logger/pino.d.mts +2 -2
- package/dist/{middleware-DqPQpcup.cjs → middleware-Bcq6XXGG.cjs} +2 -2
- package/dist/middleware-Bcq6XXGG.cjs.map +1 -0
- package/dist/{middleware-4-GrQlOT.mjs → middleware-D1XEHvyy.mjs} +2 -2
- 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-40udNP5g.mjs → otel-B9bNoeZ2.mjs} +3 -3
- package/dist/otel-B9bNoeZ2.mjs.map +1 -0
- package/dist/{otel-SBVoVEHr.d.cts → otel-Bk89b1lX.d.cts} +3 -3
- package/dist/{otel-1bJPb2Z7.d.mts.map → otel-Bk89b1lX.d.cts.map} +1 -1
- package/dist/{otel-COrpz_GZ.cjs → otel-BzjOtVxZ.cjs} +3 -3
- package/dist/otel-BzjOtVxZ.cjs.map +1 -0
- package/dist/{otel-1bJPb2Z7.d.mts → otel-lPMsM26e.d.mts} +3 -3
- package/dist/{otel-SBVoVEHr.d.cts.map → otel-lPMsM26e.d.mts.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-PnECEbEu.d.mts.map +1 -1
- package/package.json +6 -3
- package/CHANGELOG.md +0 -72
- package/dist/middleware-4-GrQlOT.mjs.map +0 -1
- package/dist/middleware-DqPQpcup.cjs.map +0 -1
- package/dist/otel-40udNP5g.mjs.map +0 -1
- package/dist/otel-COrpz_GZ.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,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
|
-
}
|