@geekmidas/telescope 9.0.2 → 10.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/README.md +14 -4
  2. package/dist/{http-D5kn3_Mt.d.mts → http-BiNtg85L.d.mts} +3 -3
  3. package/dist/{http-D5kn3_Mt.d.mts.map → http-BiNtg85L.d.mts.map} +1 -1
  4. package/dist/instrumentation/http.d.mts +1 -1
  5. package/dist/instrumentation/index.cjs +2 -2
  6. package/dist/instrumentation/index.d.cts +2 -2
  7. package/dist/instrumentation/index.d.mts +3 -3
  8. package/dist/instrumentation/index.mjs +2 -2
  9. package/dist/instrumentation/middleware.cjs +1 -1
  10. package/dist/instrumentation/middleware.d.cts +1 -1
  11. package/dist/instrumentation/middleware.d.mts +1 -1
  12. package/dist/instrumentation/middleware.mjs +1 -1
  13. package/dist/instrumentation/otel.cjs +1 -1
  14. package/dist/instrumentation/otel.d.cts +1 -1
  15. package/dist/instrumentation/otel.d.mts +1 -1
  16. package/dist/instrumentation/otel.mjs +1 -1
  17. package/dist/logger/console.d.cts.map +1 -1
  18. package/dist/logger/pino.d.mts +2 -2
  19. package/dist/{middleware-DqPQpcup.cjs → middleware-Bcq6XXGG.cjs} +2 -2
  20. package/dist/middleware-Bcq6XXGG.cjs.map +1 -0
  21. package/dist/{middleware-4-GrQlOT.mjs → middleware-D1XEHvyy.mjs} +2 -2
  22. package/dist/middleware-D1XEHvyy.mjs.map +1 -0
  23. package/dist/{middleware-CA84nsNN.d.cts → middleware-PeyS-9Ny.d.cts} +2 -2
  24. package/dist/{middleware-CA84nsNN.d.cts.map → middleware-PeyS-9Ny.d.cts.map} +1 -1
  25. package/dist/{middleware-DAceVpfw.d.mts → middleware-oHvo_ZgU.d.mts} +2 -2
  26. package/dist/{middleware-DAceVpfw.d.mts.map → middleware-oHvo_ZgU.d.mts.map} +1 -1
  27. package/dist/{otel-40udNP5g.mjs → otel-B9bNoeZ2.mjs} +3 -3
  28. package/dist/otel-B9bNoeZ2.mjs.map +1 -0
  29. package/dist/{otel-SBVoVEHr.d.cts → otel-Bk89b1lX.d.cts} +3 -3
  30. package/dist/{otel-1bJPb2Z7.d.mts.map → otel-Bk89b1lX.d.cts.map} +1 -1
  31. package/dist/{otel-COrpz_GZ.cjs → otel-BzjOtVxZ.cjs} +3 -3
  32. package/dist/otel-BzjOtVxZ.cjs.map +1 -0
  33. package/dist/{otel-1bJPb2Z7.d.mts → otel-lPMsM26e.d.mts} +3 -3
  34. package/dist/{otel-SBVoVEHr.d.cts.map → otel-lPMsM26e.d.mts.map} +1 -1
  35. package/dist/storage/kysely.cjs +48 -38
  36. package/dist/storage/kysely.cjs.map +1 -1
  37. package/dist/storage/kysely.d.cts +29 -9
  38. package/dist/storage/kysely.d.cts.map +1 -1
  39. package/dist/storage/kysely.d.mts +29 -9
  40. package/dist/storage/kysely.d.mts.map +1 -1
  41. package/dist/storage/kysely.mjs +48 -38
  42. package/dist/storage/kysely.mjs.map +1 -1
  43. package/dist/tracing-PnECEbEu.d.mts.map +1 -1
  44. package/package.json +6 -3
  45. package/CHANGELOG.md +0 -72
  46. package/dist/middleware-4-GrQlOT.mjs.map +0 -1
  47. package/dist/middleware-DqPQpcup.cjs.map +0 -1
  48. package/dist/otel-40udNP5g.mjs.map +0 -1
  49. package/dist/otel-COrpz_GZ.cjs.map +0 -1
  50. package/src/Telescope.ts +0 -561
  51. package/src/__tests__/Telescope.spec.ts +0 -438
  52. package/src/__tests__/pino-transport.spec.ts +0 -115
  53. package/src/__tests__/redact.spec.ts +0 -336
  54. package/src/adapters/__tests__/lambda.spec.ts +0 -318
  55. package/src/adapters/__tests__/middy.spec.ts +0 -741
  56. package/src/adapters/index.ts +0 -22
  57. package/src/adapters/lambda.ts +0 -432
  58. package/src/adapters/middy.ts +0 -402
  59. package/src/adapters/types.ts +0 -181
  60. package/src/core.ts +0 -51
  61. package/src/index.ts +0 -34
  62. package/src/instrumentation/__tests__/core.spec.ts +0 -248
  63. package/src/instrumentation/__tests__/hono.spec.ts +0 -348
  64. package/src/instrumentation/__tests__/middleware.spec.ts +0 -528
  65. package/src/instrumentation/__tests__/otel.spec.ts +0 -291
  66. package/src/instrumentation/__tests__/tracing.spec.ts +0 -208
  67. package/src/instrumentation/core.ts +0 -228
  68. package/src/instrumentation/hono.ts +0 -270
  69. package/src/instrumentation/http.ts +0 -306
  70. package/src/instrumentation/index.ts +0 -90
  71. package/src/instrumentation/middleware.ts +0 -297
  72. package/src/instrumentation/otel.ts +0 -271
  73. package/src/instrumentation/setup.ts +0 -246
  74. package/src/instrumentation/tracing.ts +0 -145
  75. package/src/logger/__tests__/console.spec.ts +0 -366
  76. package/src/logger/__tests__/pino.spec.ts +0 -217
  77. package/src/logger/console.ts +0 -230
  78. package/src/logger/pino.ts +0 -201
  79. package/src/metrics/MetricsAggregator.ts +0 -555
  80. package/src/metrics/__tests__/MetricsAggregator.spec.ts +0 -298
  81. package/src/metrics/index.ts +0 -2
  82. package/src/metrics/storage/InMemoryMetricsStorage.ts +0 -380
  83. package/src/metrics/storage/MetricsStorage.ts +0 -74
  84. package/src/metrics/storage/index.ts +0 -3
  85. package/src/metrics/storage/types.ts +0 -243
  86. package/src/otlp/__tests__/receiver.spec.ts +0 -367
  87. package/src/otlp/__tests__/transformer.spec.ts +0 -455
  88. package/src/otlp/hono.ts +0 -120
  89. package/src/otlp/index.ts +0 -22
  90. package/src/otlp/receiver.ts +0 -162
  91. package/src/otlp/transformer.ts +0 -367
  92. package/src/otlp/types.ts +0 -364
  93. package/src/redact.ts +0 -116
  94. package/src/server/__tests__/hono.spec.ts +0 -814
  95. package/src/server/hono.ts +0 -442
  96. package/src/storage/__tests__/kysely.spec.ts +0 -715
  97. package/src/storage/__tests__/memory.spec.ts +0 -694
  98. package/src/storage/kysely.ts +0 -599
  99. package/src/storage/memory.ts +0 -204
  100. package/src/types.ts +0 -447
  101. package/src/ui-assets.ts +0 -40
  102. package/tsconfig.json +0 -9
  103. package/ui/CHANGELOG.md +0 -7
  104. package/ui/index.html +0 -12
  105. package/ui/node_modules/.bin/tsc +0 -21
  106. package/ui/node_modules/.bin/tsserver +0 -21
  107. package/ui/node_modules/.bin/vite +0 -21
  108. package/ui/package.json +0 -24
  109. package/ui/src/App.tsx +0 -595
  110. package/ui/src/api.ts +0 -88
  111. package/ui/src/components/ExceptionDetail.tsx +0 -100
  112. package/ui/src/components/FilterBar.tsx +0 -162
  113. package/ui/src/components/LogDetail.tsx +0 -91
  114. package/ui/src/components/RequestDetail.tsx +0 -143
  115. package/ui/src/main.tsx +0 -10
  116. package/ui/src/styles.css +0 -11
  117. package/ui/src/types.ts +0 -63
  118. package/ui/src/vite-env.d.ts +0 -1
  119. package/ui/src/vite-plugin-gkm-config.ts +0 -54
  120. package/ui/tsconfig.json +0 -20
  121. package/ui/tsconfig.tsbuildinfo +0 -1
  122. package/ui/vite.config.ts +0 -13
@@ -1,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
- }