@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,228 +0,0 @@
|
|
|
1
|
-
import { DiagConsoleLogger, DiagLogLevel, diag } from '@opentelemetry/api';
|
|
2
|
-
import {
|
|
3
|
-
BatchSpanProcessor,
|
|
4
|
-
type ReadableSpan,
|
|
5
|
-
SimpleSpanProcessor,
|
|
6
|
-
type Span,
|
|
7
|
-
type SpanExporter,
|
|
8
|
-
type SpanProcessor,
|
|
9
|
-
} from '@opentelemetry/sdk-trace-base';
|
|
10
|
-
import type {
|
|
11
|
-
SpanProcessorOptions,
|
|
12
|
-
SpanProcessorStrategy,
|
|
13
|
-
} from '../adapters/types';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Global telemetry state for flush operations
|
|
17
|
-
*/
|
|
18
|
-
interface TelemetryState {
|
|
19
|
-
spanProcessor: SpanProcessor | null;
|
|
20
|
-
logProcessor: unknown | null;
|
|
21
|
-
initialized: boolean;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const state: TelemetryState = {
|
|
25
|
-
spanProcessor: null,
|
|
26
|
-
logProcessor: null,
|
|
27
|
-
initialized: false,
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Create a span processor based on the strategy
|
|
32
|
-
*/
|
|
33
|
-
export function createSpanProcessor(
|
|
34
|
-
exporter: SpanExporter,
|
|
35
|
-
options: SpanProcessorOptions,
|
|
36
|
-
): SpanProcessor {
|
|
37
|
-
const { strategy, ...config } = options;
|
|
38
|
-
|
|
39
|
-
if (strategy === 'simple') {
|
|
40
|
-
return new SimpleSpanProcessor(exporter);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return new BatchSpanProcessor(exporter, {
|
|
44
|
-
maxQueueSize: config.maxQueueSize ?? 2048,
|
|
45
|
-
scheduledDelayMillis: config.scheduledDelayMillis ?? 5000,
|
|
46
|
-
exportTimeoutMillis: config.exportTimeoutMillis ?? 30000,
|
|
47
|
-
maxExportBatchSize: config.maxExportBatchSize ?? 512,
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Get the recommended span processor strategy for an environment
|
|
53
|
-
*/
|
|
54
|
-
export function getRecommendedStrategy(
|
|
55
|
-
environment: 'server' | 'lambda' | 'edge' | 'custom',
|
|
56
|
-
): SpanProcessorStrategy {
|
|
57
|
-
switch (environment) {
|
|
58
|
-
case 'lambda':
|
|
59
|
-
case 'edge':
|
|
60
|
-
// Use simple processor for serverless - immediate export
|
|
61
|
-
return 'simple';
|
|
62
|
-
default:
|
|
63
|
-
// Use batch processor for long-running servers - efficient batching
|
|
64
|
-
return 'batch';
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Set the global span processor for flush operations
|
|
70
|
-
*/
|
|
71
|
-
export function setGlobalSpanProcessor(processor: SpanProcessor): void {
|
|
72
|
-
state.spanProcessor = processor;
|
|
73
|
-
state.initialized = true;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Set the global log processor for flush operations
|
|
78
|
-
*/
|
|
79
|
-
export function setGlobalLogProcessor(processor: unknown): void {
|
|
80
|
-
state.logProcessor = processor;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Check if telemetry is initialized
|
|
85
|
-
*/
|
|
86
|
-
export function isTelemetryInitialized(): boolean {
|
|
87
|
-
return state.initialized;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Flush all pending telemetry data.
|
|
92
|
-
* This is critical for serverless environments where the process may freeze.
|
|
93
|
-
*
|
|
94
|
-
* @example
|
|
95
|
-
* ```typescript
|
|
96
|
-
* // In a Lambda handler
|
|
97
|
-
* export const handler = async (event, context) => {
|
|
98
|
-
* try {
|
|
99
|
-
* const result = await processRequest(event);
|
|
100
|
-
* return result;
|
|
101
|
-
* } finally {
|
|
102
|
-
* // Flush before Lambda freezes
|
|
103
|
-
* await flushTelemetry();
|
|
104
|
-
* }
|
|
105
|
-
* };
|
|
106
|
-
* ```
|
|
107
|
-
*/
|
|
108
|
-
export async function flushTelemetry(timeoutMs = 30000): Promise<void> {
|
|
109
|
-
const promises: Promise<void>[] = [];
|
|
110
|
-
|
|
111
|
-
// Flush span processor
|
|
112
|
-
if (state.spanProcessor) {
|
|
113
|
-
promises.push(
|
|
114
|
-
Promise.race([
|
|
115
|
-
state.spanProcessor.forceFlush(),
|
|
116
|
-
new Promise<void>((_, reject) =>
|
|
117
|
-
setTimeout(() => reject(new Error('Span flush timeout')), timeoutMs),
|
|
118
|
-
),
|
|
119
|
-
]).catch((error) => {
|
|
120
|
-
if (diag.debug) {
|
|
121
|
-
diag.debug(`Span flush error: ${error.message}`);
|
|
122
|
-
}
|
|
123
|
-
}),
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Flush log processor if it has forceFlush method
|
|
128
|
-
if (
|
|
129
|
-
state.logProcessor &&
|
|
130
|
-
typeof (state.logProcessor as { forceFlush?: () => Promise<void> })
|
|
131
|
-
.forceFlush === 'function'
|
|
132
|
-
) {
|
|
133
|
-
promises.push(
|
|
134
|
-
Promise.race([
|
|
135
|
-
(
|
|
136
|
-
state.logProcessor as { forceFlush: () => Promise<void> }
|
|
137
|
-
).forceFlush(),
|
|
138
|
-
new Promise<void>((_, reject) =>
|
|
139
|
-
setTimeout(() => reject(new Error('Log flush timeout')), timeoutMs),
|
|
140
|
-
),
|
|
141
|
-
]).catch((error) => {
|
|
142
|
-
if (diag.debug) {
|
|
143
|
-
diag.debug(`Log flush error: ${error.message}`);
|
|
144
|
-
}
|
|
145
|
-
}),
|
|
146
|
-
);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
await Promise.all(promises);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Shutdown all telemetry processors
|
|
154
|
-
*/
|
|
155
|
-
export async function shutdownTelemetry(): Promise<void> {
|
|
156
|
-
const promises: Promise<void>[] = [];
|
|
157
|
-
|
|
158
|
-
if (state.spanProcessor) {
|
|
159
|
-
promises.push(state.spanProcessor.shutdown());
|
|
160
|
-
state.spanProcessor = null;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
if (
|
|
164
|
-
state.logProcessor &&
|
|
165
|
-
typeof (state.logProcessor as { shutdown?: () => Promise<void> })
|
|
166
|
-
.shutdown === 'function'
|
|
167
|
-
) {
|
|
168
|
-
promises.push(
|
|
169
|
-
(state.logProcessor as { shutdown: () => Promise<void> }).shutdown(),
|
|
170
|
-
);
|
|
171
|
-
state.logProcessor = null;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
state.initialized = false;
|
|
175
|
-
await Promise.all(promises);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Enable debug logging for telemetry
|
|
180
|
-
*/
|
|
181
|
-
export function enableTelemetryDebug(): void {
|
|
182
|
-
diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.DEBUG);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Wraps a handler function to ensure telemetry is flushed before returning.
|
|
187
|
-
* Useful for Lambda handlers.
|
|
188
|
-
*
|
|
189
|
-
* @example
|
|
190
|
-
* ```typescript
|
|
191
|
-
* export const handler = withTelemetryFlush(async (event, context) => {
|
|
192
|
-
* // Your Lambda logic here
|
|
193
|
-
* return { statusCode: 200, body: 'OK' };
|
|
194
|
-
* });
|
|
195
|
-
* ```
|
|
196
|
-
*/
|
|
197
|
-
export function withTelemetryFlush<TEvent, TResult>(
|
|
198
|
-
handler: (event: TEvent, context: unknown) => Promise<TResult>,
|
|
199
|
-
): (event: TEvent, context: unknown) => Promise<TResult> {
|
|
200
|
-
return async (event: TEvent, context: unknown): Promise<TResult> => {
|
|
201
|
-
try {
|
|
202
|
-
return await handler(event, context);
|
|
203
|
-
} finally {
|
|
204
|
-
await flushTelemetry();
|
|
205
|
-
}
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Noop span processor for testing or when telemetry is disabled
|
|
211
|
-
*/
|
|
212
|
-
export class NoopSpanProcessor implements SpanProcessor {
|
|
213
|
-
forceFlush(): Promise<void> {
|
|
214
|
-
return Promise.resolve();
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
onStart(_span: Span): void {
|
|
218
|
-
// noop
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
onEnd(_span: ReadableSpan): void {
|
|
222
|
-
// noop
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
shutdown(): Promise<void> {
|
|
226
|
-
return Promise.resolve();
|
|
227
|
-
}
|
|
228
|
-
}
|
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
import { context, type Span, trace } from '@opentelemetry/api';
|
|
2
|
-
/**
|
|
3
|
-
* OpenTelemetry instrumentation middleware for Hono
|
|
4
|
-
*
|
|
5
|
-
* This middleware automatically creates spans for HTTP requests,
|
|
6
|
-
* extracts trace context from incoming headers, and records
|
|
7
|
-
* request/response metadata as span attributes.
|
|
8
|
-
*/
|
|
9
|
-
import type { Context, MiddlewareHandler } from 'hono';
|
|
10
|
-
import {
|
|
11
|
-
createHttpServerSpan,
|
|
12
|
-
endHttpSpan,
|
|
13
|
-
extractTraceContext,
|
|
14
|
-
type HttpSpanAttributes,
|
|
15
|
-
} from './http';
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Options for the Hono telemetry middleware
|
|
19
|
-
*/
|
|
20
|
-
export interface HonoTelemetryMiddlewareOptions {
|
|
21
|
-
/**
|
|
22
|
-
* Whether to record request body as span attribute
|
|
23
|
-
* @default false
|
|
24
|
-
*/
|
|
25
|
-
recordBody?: boolean;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Whether to record response body as span attribute
|
|
29
|
-
* @default false
|
|
30
|
-
*/
|
|
31
|
-
recordResponseBody?: boolean;
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Custom function to extract user ID from context
|
|
35
|
-
*/
|
|
36
|
-
getUserId?: (c: Context) => string | undefined;
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Paths to skip tracing (supports wildcards)
|
|
40
|
-
*/
|
|
41
|
-
ignorePaths?: string[];
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Whether to skip tracing for this request
|
|
45
|
-
*/
|
|
46
|
-
shouldSkip?: (c: Context) => boolean;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Key for storing span on context
|
|
50
|
-
const SPAN_KEY = 'telemetry.span';
|
|
51
|
-
const CONTEXT_KEY = 'telemetry.context';
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Get the current span from Hono context
|
|
55
|
-
*/
|
|
56
|
-
export function getSpanFromContext(c: Context): Span | undefined {
|
|
57
|
-
return c.get(SPAN_KEY);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Get the current trace context from Hono context
|
|
62
|
-
*/
|
|
63
|
-
export function getTraceContextFromHono(c: Context): any {
|
|
64
|
-
return c.get(CONTEXT_KEY);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Create a telemetry middleware for Hono applications
|
|
69
|
-
*
|
|
70
|
-
* @example
|
|
71
|
-
* ```typescript
|
|
72
|
-
* import { honoTelemetryMiddleware } from '@geekmidas/telescope/instrumentation';
|
|
73
|
-
*
|
|
74
|
-
* const app = new Hono();
|
|
75
|
-
* app.use('*', honoTelemetryMiddleware());
|
|
76
|
-
* ```
|
|
77
|
-
*/
|
|
78
|
-
export function honoTelemetryMiddleware(
|
|
79
|
-
options: HonoTelemetryMiddlewareOptions = {},
|
|
80
|
-
): MiddlewareHandler {
|
|
81
|
-
return async (c, next) => {
|
|
82
|
-
// Check if path should be ignored
|
|
83
|
-
if (
|
|
84
|
-
options.ignorePaths &&
|
|
85
|
-
shouldIgnorePath(c.req.path, options.ignorePaths)
|
|
86
|
-
) {
|
|
87
|
-
return next();
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Skip if custom skip function returns true
|
|
91
|
-
if (options.shouldSkip?.(c)) {
|
|
92
|
-
return next();
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Extract trace context from headers
|
|
96
|
-
const headers: Record<string, string | string[] | undefined> = {};
|
|
97
|
-
c.req.raw.headers.forEach((value, key) => {
|
|
98
|
-
headers[key] = value;
|
|
99
|
-
});
|
|
100
|
-
const parentContext = extractTraceContext(headers);
|
|
101
|
-
|
|
102
|
-
// Build span attributes from request
|
|
103
|
-
const attrs = buildHonoSpanAttributes(c, options);
|
|
104
|
-
|
|
105
|
-
// Create the span
|
|
106
|
-
const span = createHttpServerSpan(attrs, parentContext);
|
|
107
|
-
const spanContext = trace.setSpan(parentContext, span);
|
|
108
|
-
|
|
109
|
-
// Store span and context on Hono context
|
|
110
|
-
c.set(SPAN_KEY, span);
|
|
111
|
-
c.set(CONTEXT_KEY, spanContext);
|
|
112
|
-
|
|
113
|
-
// Record body if requested
|
|
114
|
-
if (options.recordBody) {
|
|
115
|
-
try {
|
|
116
|
-
const body = await c.req.text();
|
|
117
|
-
if (body) {
|
|
118
|
-
span.setAttribute(
|
|
119
|
-
'http.request.body',
|
|
120
|
-
body.length > 4096 ? `${body.slice(0, 4096)}...` : body,
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
} catch {
|
|
124
|
-
// Body may have already been consumed
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
try {
|
|
129
|
-
// Execute the rest of the middleware chain within span context
|
|
130
|
-
await context.with(spanContext, async () => {
|
|
131
|
-
await next();
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
// Record response
|
|
135
|
-
const statusCode = c.res.status;
|
|
136
|
-
|
|
137
|
-
if (options.recordResponseBody) {
|
|
138
|
-
try {
|
|
139
|
-
const body = await c.res.clone().text();
|
|
140
|
-
if (body) {
|
|
141
|
-
span.setAttribute(
|
|
142
|
-
'http.response.body',
|
|
143
|
-
body.length > 4096 ? `${body.slice(0, 4096)}...` : body,
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
} catch {
|
|
147
|
-
// Response body may not be available
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
endHttpSpan(span, { statusCode });
|
|
152
|
-
} catch (error) {
|
|
153
|
-
endHttpSpan(
|
|
154
|
-
span,
|
|
155
|
-
{ statusCode: 500 },
|
|
156
|
-
error instanceof Error ? error : new Error(String(error)),
|
|
157
|
-
);
|
|
158
|
-
throw error;
|
|
159
|
-
}
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Build span attributes from Hono context
|
|
165
|
-
*/
|
|
166
|
-
function buildHonoSpanAttributes(
|
|
167
|
-
c: Context,
|
|
168
|
-
options: HonoTelemetryMiddlewareOptions,
|
|
169
|
-
): HttpSpanAttributes {
|
|
170
|
-
const req = c.req;
|
|
171
|
-
const url = new URL(req.url);
|
|
172
|
-
|
|
173
|
-
// Use actual path for route unless it's a specific route pattern
|
|
174
|
-
const routePath = c.req.routePath;
|
|
175
|
-
const route =
|
|
176
|
-
routePath && routePath !== '*' && !routePath.includes('/*')
|
|
177
|
-
? routePath
|
|
178
|
-
: url.pathname;
|
|
179
|
-
|
|
180
|
-
const attrs: HttpSpanAttributes = {
|
|
181
|
-
method: req.method,
|
|
182
|
-
url: req.url,
|
|
183
|
-
path: url.pathname,
|
|
184
|
-
route,
|
|
185
|
-
host: url.host,
|
|
186
|
-
scheme: url.protocol.replace(':', '') as 'http' | 'https',
|
|
187
|
-
userAgent: req.header('user-agent'),
|
|
188
|
-
clientIp: getClientIp(c),
|
|
189
|
-
requestId: req.header('x-request-id') || req.header('x-amzn-requestid'),
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
// Add user metadata if extractor provided
|
|
193
|
-
if (options.getUserId) {
|
|
194
|
-
const userId = options.getUserId(c);
|
|
195
|
-
if (userId) {
|
|
196
|
-
attrs.user = { userId };
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
return attrs;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Extract client IP from Hono context
|
|
205
|
-
*/
|
|
206
|
-
function getClientIp(c: Context): string | undefined {
|
|
207
|
-
// Try various headers in order of preference
|
|
208
|
-
const xForwardedFor = c.req.header('x-forwarded-for');
|
|
209
|
-
if (xForwardedFor) {
|
|
210
|
-
const first = xForwardedFor.split(',')[0];
|
|
211
|
-
return first?.trim();
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
const xRealIp = c.req.header('x-real-ip');
|
|
215
|
-
if (xRealIp) {
|
|
216
|
-
return xRealIp;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// Try CF-Connecting-IP (Cloudflare)
|
|
220
|
-
const cfConnectingIp = c.req.header('cf-connecting-ip');
|
|
221
|
-
if (cfConnectingIp) {
|
|
222
|
-
return cfConnectingIp;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
return undefined;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Check if path should be ignored
|
|
230
|
-
*/
|
|
231
|
-
function shouldIgnorePath(path: string, ignorePaths: string[]): boolean {
|
|
232
|
-
for (const pattern of ignorePaths) {
|
|
233
|
-
if (pattern.endsWith('*')) {
|
|
234
|
-
// Wildcard match
|
|
235
|
-
const prefix = pattern.slice(0, -1);
|
|
236
|
-
if (path.startsWith(prefix)) {
|
|
237
|
-
return true;
|
|
238
|
-
}
|
|
239
|
-
} else if (path === pattern) {
|
|
240
|
-
// Exact match
|
|
241
|
-
return true;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
return false;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Run a function within the span context from Hono context
|
|
249
|
-
*
|
|
250
|
-
* @example
|
|
251
|
-
* ```typescript
|
|
252
|
-
* app.get('/api/users', async (c) => {
|
|
253
|
-
* return withHonoSpanContext(c, async () => {
|
|
254
|
-
* // Any spans created here will be children of the request span
|
|
255
|
-
* const users = await fetchUsers();
|
|
256
|
-
* return c.json(users);
|
|
257
|
-
* });
|
|
258
|
-
* });
|
|
259
|
-
* ```
|
|
260
|
-
*/
|
|
261
|
-
export async function withHonoSpanContext<T>(
|
|
262
|
-
c: Context,
|
|
263
|
-
fn: () => Promise<T>,
|
|
264
|
-
): Promise<T> {
|
|
265
|
-
const ctx = getTraceContextFromHono(c);
|
|
266
|
-
if (!ctx) {
|
|
267
|
-
return fn();
|
|
268
|
-
}
|
|
269
|
-
return context.with(ctx, fn);
|
|
270
|
-
}
|