@geekmidas/telescope 0.0.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 +521 -0
- package/dist/Telescope-B3Wd82yk.cjs +602 -0
- package/dist/Telescope-B3Wd82yk.cjs.map +1 -0
- package/dist/Telescope-C5dyDYYB.d.cts +133 -0
- package/dist/Telescope-D-uoZB6b.mjs +596 -0
- package/dist/Telescope-D-uoZB6b.mjs.map +1 -0
- package/dist/Telescope-DyIWgh9-.d.mts +133 -0
- package/dist/Telescope.cjs +3 -0
- package/dist/Telescope.d.cts +3 -0
- package/dist/Telescope.d.mts +3 -0
- package/dist/Telescope.mjs +3 -0
- package/dist/chunk-CUT6urMc.cjs +30 -0
- package/dist/index.cjs +5 -0
- package/dist/index.d.cts +4 -0
- package/dist/index.d.mts +4 -0
- package/dist/index.mjs +4 -0
- package/dist/logger/console.cjs +161 -0
- package/dist/logger/console.cjs.map +1 -0
- package/dist/logger/console.d.cts +109 -0
- package/dist/logger/console.d.mts +109 -0
- package/dist/logger/console.mjs +159 -0
- package/dist/logger/console.mjs.map +1 -0
- package/dist/logger/pino.cjs +118 -0
- package/dist/logger/pino.cjs.map +1 -0
- package/dist/logger/pino.d.cts +89 -0
- package/dist/logger/pino.d.mts +89 -0
- package/dist/logger/pino.mjs +116 -0
- package/dist/logger/pino.mjs.map +1 -0
- package/dist/memory-9-B9WACq.cjs +110 -0
- package/dist/memory-9-B9WACq.cjs.map +1 -0
- package/dist/memory-Cm0eevCS.d.mts +38 -0
- package/dist/memory-DiP1a-pp.d.cts +38 -0
- package/dist/memory-SdN5vtG9.mjs +104 -0
- package/dist/memory-SdN5vtG9.mjs.map +1 -0
- package/dist/server/hono.cjs +180 -0
- package/dist/server/hono.cjs.map +1 -0
- package/dist/server/hono.d.cts +26 -0
- package/dist/server/hono.d.mts +26 -0
- package/dist/server/hono.mjs +176 -0
- package/dist/server/hono.mjs.map +1 -0
- package/dist/storage/kysely.cjs +336 -0
- package/dist/storage/kysely.cjs.map +1 -0
- package/dist/storage/kysely.d.cts +161 -0
- package/dist/storage/kysely.d.mts +161 -0
- package/dist/storage/kysely.mjs +334 -0
- package/dist/storage/kysely.mjs.map +1 -0
- package/dist/storage/memory.cjs +3 -0
- package/dist/storage/memory.d.cts +3 -0
- package/dist/storage/memory.d.mts +3 -0
- package/dist/storage/memory.mjs +3 -0
- package/dist/types-BGDhFv4R.d.cts +170 -0
- package/dist/types-CZbzz8kx.d.mts +170 -0
- package/dist/types.cjs +0 -0
- package/dist/types.d.cts +2 -0
- package/dist/types.d.mts +2 -0
- package/dist/types.mjs +0 -0
- package/dist/ui-assets-D6-8TAr_.mjs +30 -0
- package/dist/ui-assets-D6-8TAr_.mjs.map +1 -0
- package/dist/ui-assets-ulevVble.cjs +48 -0
- package/dist/ui-assets-ulevVble.cjs.map +1 -0
- package/dist/ui-assets.cjs +5 -0
- package/dist/ui-assets.d.cts +12 -0
- package/dist/ui-assets.d.mts +12 -0
- package/dist/ui-assets.mjs +3 -0
- package/package.json +83 -0
- package/scripts/embed-ui.ts +90 -0
- package/src/Telescope.ts +714 -0
- package/src/__tests__/Telescope.spec.ts +356 -0
- package/src/index.ts +23 -0
- package/src/logger/__tests__/console.spec.ts +266 -0
- package/src/logger/__tests__/pino.spec.ts +217 -0
- package/src/logger/console.ts +230 -0
- package/src/logger/pino.ts +191 -0
- package/src/server/__tests__/hono.spec.ts +340 -0
- package/src/server/hono.ts +247 -0
- package/src/storage/__tests__/kysely.spec.ts +715 -0
- package/src/storage/__tests__/memory.spec.ts +411 -0
- package/src/storage/kysely.ts +572 -0
- package/src/storage/memory.ts +168 -0
- package/src/types.ts +188 -0
- package/src/ui-assets.ts +40 -0
- package/ui/index.html +12 -0
- package/ui/node_modules/.bin/browserslist +21 -0
- package/ui/node_modules/.bin/jiti +21 -0
- package/ui/node_modules/.bin/terser +21 -0
- package/ui/node_modules/.bin/tsc +21 -0
- package/ui/node_modules/.bin/tsserver +21 -0
- package/ui/node_modules/.bin/tsx +21 -0
- package/ui/node_modules/.bin/vite +21 -0
- package/ui/package.json +24 -0
- package/ui/src/App.tsx +342 -0
- package/ui/src/api.ts +75 -0
- package/ui/src/components/ExceptionDetail.tsx +100 -0
- package/ui/src/components/LogDetail.tsx +91 -0
- package/ui/src/components/RequestDetail.tsx +143 -0
- package/ui/src/main.tsx +10 -0
- package/ui/src/styles.css +10 -0
- package/ui/src/types.ts +63 -0
- package/ui/src/vite-env.d.ts +1 -0
- package/ui/src/vite-plugin-gkm-config.ts +54 -0
- package/ui/tsconfig.json +20 -0
- package/ui/tsconfig.tsbuildinfo +14 -0
- package/ui/vite.config.ts +13 -0
package/README.md
ADDED
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
# @geekmidas/telescope
|
|
2
|
+
|
|
3
|
+
Laravel Telescope-style debugging and monitoring for web applications. Capture and inspect HTTP requests, exceptions, and logs in real-time.
|
|
4
|
+
|
|
5
|
+
**Framework-agnostic core** with adapters for popular frameworks like Hono (Express coming soon).
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add @geekmidas/telescope
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
### With Hono
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { Hono } from 'hono';
|
|
19
|
+
import { Telescope, InMemoryStorage } from '@geekmidas/telescope';
|
|
20
|
+
import { createMiddleware, createUI } from '@geekmidas/telescope/server/hono';
|
|
21
|
+
|
|
22
|
+
const telescope = new Telescope({
|
|
23
|
+
storage: new InMemoryStorage(),
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const app = new Hono();
|
|
27
|
+
|
|
28
|
+
// Add middleware to capture requests
|
|
29
|
+
app.use('*', createMiddleware(telescope));
|
|
30
|
+
|
|
31
|
+
// Mount the dashboard
|
|
32
|
+
app.route('/__telescope', createUI(telescope));
|
|
33
|
+
|
|
34
|
+
// Your routes
|
|
35
|
+
app.get('/users', (c) => c.json({ users: [] }));
|
|
36
|
+
|
|
37
|
+
export default app;
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Visit `http://localhost:3000/__telescope` to view the dashboard.
|
|
41
|
+
|
|
42
|
+
### Framework-Agnostic Core
|
|
43
|
+
|
|
44
|
+
The core `Telescope` class is framework-agnostic. You can use it directly for manual recording:
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import { Telescope, InMemoryStorage } from '@geekmidas/telescope';
|
|
48
|
+
|
|
49
|
+
const telescope = new Telescope({
|
|
50
|
+
storage: new InMemoryStorage(),
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Record a request manually
|
|
54
|
+
await telescope.recordRequest({
|
|
55
|
+
method: 'POST',
|
|
56
|
+
path: '/api/users',
|
|
57
|
+
url: 'http://localhost:3000/api/users',
|
|
58
|
+
headers: { 'content-type': 'application/json' },
|
|
59
|
+
body: { name: 'John' },
|
|
60
|
+
query: {},
|
|
61
|
+
status: 201,
|
|
62
|
+
responseHeaders: {},
|
|
63
|
+
responseBody: { id: '123' },
|
|
64
|
+
duration: 45.2,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Log messages
|
|
68
|
+
await telescope.log('info', 'User created', { userId: '123' });
|
|
69
|
+
|
|
70
|
+
// Record exceptions
|
|
71
|
+
try {
|
|
72
|
+
throw new Error('Something went wrong');
|
|
73
|
+
} catch (error) {
|
|
74
|
+
await telescope.exception(error);
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Features
|
|
79
|
+
|
|
80
|
+
- **Request Recording**: Capture HTTP requests with headers, body, query params, and response
|
|
81
|
+
- **Exception Tracking**: Record exceptions with stack traces and source context
|
|
82
|
+
- **Log Aggregation**: Collect application logs with context
|
|
83
|
+
- **Real-time Updates**: WebSocket-powered live dashboard
|
|
84
|
+
- **Storage Agnostic**: Use in-memory for dev, database for production
|
|
85
|
+
- **Hono Integration**: First-class middleware and route mounting
|
|
86
|
+
|
|
87
|
+
## Configuration
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
const telescope = new Telescope({
|
|
91
|
+
// Required: Storage backend
|
|
92
|
+
storage: new InMemoryStorage(),
|
|
93
|
+
|
|
94
|
+
// Optional: Enable/disable telescope (default: true)
|
|
95
|
+
enabled: true,
|
|
96
|
+
|
|
97
|
+
// Optional: Dashboard path (default: '/__telescope')
|
|
98
|
+
path: '/__telescope',
|
|
99
|
+
|
|
100
|
+
// Optional: Record request/response bodies (default: true)
|
|
101
|
+
recordBody: true,
|
|
102
|
+
|
|
103
|
+
// Optional: Max body size to record in bytes (default: 64KB)
|
|
104
|
+
maxBodySize: 64 * 1024,
|
|
105
|
+
|
|
106
|
+
// Optional: URL patterns to ignore
|
|
107
|
+
ignorePatterns: ['/health', '/metrics', '/__telescope/*'],
|
|
108
|
+
|
|
109
|
+
// Optional: Auto-prune entries older than X hours
|
|
110
|
+
pruneAfterHours: 24,
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Storage Backends
|
|
115
|
+
|
|
116
|
+
### InMemoryStorage
|
|
117
|
+
|
|
118
|
+
Best for development and testing. Data is lost on restart.
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
// From main package
|
|
122
|
+
import { InMemoryStorage } from '@geekmidas/telescope';
|
|
123
|
+
// Or direct import
|
|
124
|
+
import { InMemoryStorage } from '@geekmidas/telescope/storage/memory';
|
|
125
|
+
|
|
126
|
+
const storage = new InMemoryStorage({
|
|
127
|
+
maxEntries: 1000, // Max entries per type (default: 1000)
|
|
128
|
+
});
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### KyselyStorage (Coming Soon)
|
|
132
|
+
|
|
133
|
+
For production use with database persistence.
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
import { KyselyStorage } from '@geekmidas/telescope/storage/kysely';
|
|
137
|
+
|
|
138
|
+
const storage = new KyselyStorage(db, {
|
|
139
|
+
tablePrefix: 'telescope_', // Table name prefix
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Custom Storage
|
|
144
|
+
|
|
145
|
+
Implement the `TelescopeStorage` interface:
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
import type { TelescopeStorage } from '@geekmidas/telescope';
|
|
149
|
+
|
|
150
|
+
class MyStorage implements TelescopeStorage {
|
|
151
|
+
async saveRequest(entry: RequestEntry): Promise<void> { /* ... */ }
|
|
152
|
+
async getRequests(options?: QueryOptions): Promise<RequestEntry[]> { /* ... */ }
|
|
153
|
+
async getRequest(id: string): Promise<RequestEntry | null> { /* ... */ }
|
|
154
|
+
|
|
155
|
+
async saveException(entry: ExceptionEntry): Promise<void> { /* ... */ }
|
|
156
|
+
async getExceptions(options?: QueryOptions): Promise<ExceptionEntry[]> { /* ... */ }
|
|
157
|
+
async getException(id: string): Promise<ExceptionEntry | null> { /* ... */ }
|
|
158
|
+
|
|
159
|
+
async saveLog(entry: LogEntry): Promise<void> { /* ... */ }
|
|
160
|
+
async getLogs(options?: QueryOptions): Promise<LogEntry[]> { /* ... */ }
|
|
161
|
+
|
|
162
|
+
async prune(olderThan: Date): Promise<number> { /* ... */ }
|
|
163
|
+
async getStats(): Promise<TelescopeStats> { /* ... */ }
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## API
|
|
168
|
+
|
|
169
|
+
### Telescope Class (Core)
|
|
170
|
+
|
|
171
|
+
#### `telescope.recordRequest(entry)`
|
|
172
|
+
|
|
173
|
+
Record a request entry programmatically.
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
await telescope.recordRequest({
|
|
177
|
+
method: 'GET',
|
|
178
|
+
path: '/api/users',
|
|
179
|
+
url: 'http://localhost:3000/api/users',
|
|
180
|
+
headers: {},
|
|
181
|
+
query: {},
|
|
182
|
+
status: 200,
|
|
183
|
+
responseHeaders: {},
|
|
184
|
+
duration: 25.5,
|
|
185
|
+
});
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
#### `telescope.log(level, message, context?, requestId?)`
|
|
189
|
+
|
|
190
|
+
Record a log entry programmatically.
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
await telescope.log('info', 'User logged in', { userId: '123' });
|
|
194
|
+
await telescope.log('error', 'Payment failed', { orderId: '456' });
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
#### `telescope.exception(error, requestId?)`
|
|
198
|
+
|
|
199
|
+
Record an exception programmatically.
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
try {
|
|
203
|
+
await riskyOperation();
|
|
204
|
+
} catch (error) {
|
|
205
|
+
await telescope.exception(error);
|
|
206
|
+
throw error;
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
#### `telescope.broadcast(event)`
|
|
211
|
+
|
|
212
|
+
Send a custom event to all connected WebSocket clients.
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
telescope.broadcast({
|
|
216
|
+
type: 'custom',
|
|
217
|
+
payload: { message: 'Hello' },
|
|
218
|
+
timestamp: Date.now(),
|
|
219
|
+
});
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
#### `telescope.prune(olderThan)`
|
|
223
|
+
|
|
224
|
+
Manually prune entries older than a date.
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000);
|
|
228
|
+
const deleted = await telescope.prune(oneHourAgo);
|
|
229
|
+
console.log(`Pruned ${deleted} entries`);
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
#### `telescope.destroy()`
|
|
233
|
+
|
|
234
|
+
Clean up resources (intervals, WebSocket clients).
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
telescope.destroy();
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Hono Adapter
|
|
241
|
+
|
|
242
|
+
Import from `@geekmidas/telescope/server/hono`:
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
import { createMiddleware, createUI, setupWebSocket, getRequestId } from '@geekmidas/telescope/server/hono';
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
#### `createMiddleware(telescope)`
|
|
249
|
+
|
|
250
|
+
Returns Hono middleware that captures requests and responses.
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
app.use('*', createMiddleware(telescope));
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
#### `createUI(telescope)`
|
|
257
|
+
|
|
258
|
+
Returns a Hono app with dashboard UI and API routes.
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
app.route('/__telescope', createUI(telescope));
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
#### `setupWebSocket(app, telescope, upgradeWebSocket)`
|
|
265
|
+
|
|
266
|
+
Set up WebSocket routes for real-time updates.
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
import { createNodeWebSocket } from '@hono/node-ws';
|
|
270
|
+
|
|
271
|
+
const { upgradeWebSocket } = createNodeWebSocket({ app });
|
|
272
|
+
setupWebSocket(createUI(telescope), telescope, upgradeWebSocket);
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
#### `getRequestId(c)`
|
|
276
|
+
|
|
277
|
+
Get the request ID from Hono context (set by middleware).
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
app.get('/api/data', (c) => {
|
|
281
|
+
const requestId = getRequestId(c);
|
|
282
|
+
// Use requestId for correlated logging
|
|
283
|
+
telescope.log('info', 'Processing request', { data: 'value' }, requestId);
|
|
284
|
+
return c.json({ success: true });
|
|
285
|
+
});
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Logger Integrations
|
|
289
|
+
|
|
290
|
+
Telescope integrates with existing logging libraries so you don't need to change your application code.
|
|
291
|
+
|
|
292
|
+
#### Pino Transport
|
|
293
|
+
|
|
294
|
+
Use with Pino's multistream to send logs to both stdout and Telescope:
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
import pino from 'pino';
|
|
298
|
+
import { Telescope, InMemoryStorage } from '@geekmidas/telescope';
|
|
299
|
+
import { createPinoDestination } from '@geekmidas/telescope/logger/pino';
|
|
300
|
+
|
|
301
|
+
const telescope = new Telescope({ storage: new InMemoryStorage() });
|
|
302
|
+
|
|
303
|
+
const logger = pino(
|
|
304
|
+
{ level: 'debug' },
|
|
305
|
+
pino.multistream([
|
|
306
|
+
{ stream: process.stdout },
|
|
307
|
+
{ stream: createPinoDestination({ telescope }) }
|
|
308
|
+
])
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
// Logs go to both console and Telescope
|
|
312
|
+
logger.info({ userId: '123' }, 'User logged in');
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
#### TelescopeLogger (ConsoleLogger wrapper)
|
|
316
|
+
|
|
317
|
+
Wraps any Logger interface and forwards logs to Telescope:
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
import { Telescope, InMemoryStorage } from '@geekmidas/telescope';
|
|
321
|
+
import { TelescopeLogger, createTelescopeLogger } from '@geekmidas/telescope/logger/console';
|
|
322
|
+
import { ConsoleLogger } from '@geekmidas/logger/console';
|
|
323
|
+
|
|
324
|
+
const telescope = new Telescope({ storage: new InMemoryStorage() });
|
|
325
|
+
|
|
326
|
+
// Option 1: Using the class
|
|
327
|
+
const logger = new TelescopeLogger({
|
|
328
|
+
telescope,
|
|
329
|
+
logger: new ConsoleLogger({ app: 'myApp' }),
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
// Option 2: Using the factory function
|
|
333
|
+
const logger = createTelescopeLogger(telescope, new ConsoleLogger());
|
|
334
|
+
|
|
335
|
+
// Logs go to both console and Telescope
|
|
336
|
+
logger.info({ action: 'startup' }, 'Application started');
|
|
337
|
+
|
|
338
|
+
// Create child loggers with context
|
|
339
|
+
const authLogger = logger.child({ module: 'auth' });
|
|
340
|
+
authLogger.debug({ userId: '123' }, 'User authenticated');
|
|
341
|
+
|
|
342
|
+
// Bind to a request ID for correlation
|
|
343
|
+
const requestLogger = logger.withRequestId('req-abc123');
|
|
344
|
+
requestLogger.info('Processing request');
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
## Dashboard API
|
|
348
|
+
|
|
349
|
+
The dashboard UI is served at the configured path. It also exposes a REST API:
|
|
350
|
+
|
|
351
|
+
| Endpoint | Description |
|
|
352
|
+
|----------|-------------|
|
|
353
|
+
| `GET /api/requests` | List requests |
|
|
354
|
+
| `GET /api/requests/:id` | Get request details |
|
|
355
|
+
| `GET /api/exceptions` | List exceptions |
|
|
356
|
+
| `GET /api/exceptions/:id` | Get exception details |
|
|
357
|
+
| `GET /api/logs` | List logs |
|
|
358
|
+
| `GET /api/stats` | Get storage statistics |
|
|
359
|
+
|
|
360
|
+
### Query Parameters
|
|
361
|
+
|
|
362
|
+
All list endpoints support:
|
|
363
|
+
|
|
364
|
+
- `limit` - Number of entries (default: 50, max: 100)
|
|
365
|
+
- `offset` - Pagination offset (default: 0)
|
|
366
|
+
- `search` - Full-text search
|
|
367
|
+
- `before` - Entries before date (ISO string)
|
|
368
|
+
- `after` - Entries after date (ISO string)
|
|
369
|
+
- `tags` - Comma-separated tags filter
|
|
370
|
+
|
|
371
|
+
Example:
|
|
372
|
+
```
|
|
373
|
+
GET /__telescope/api/requests?limit=20&search=POST&after=2024-01-01
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
## WebSocket
|
|
377
|
+
|
|
378
|
+
Connect to `/__telescope/ws` for real-time updates:
|
|
379
|
+
|
|
380
|
+
```typescript
|
|
381
|
+
const ws = new WebSocket('ws://localhost:3000/__telescope/ws');
|
|
382
|
+
|
|
383
|
+
ws.onmessage = (event) => {
|
|
384
|
+
const { type, payload, timestamp } = JSON.parse(event.data);
|
|
385
|
+
|
|
386
|
+
switch (type) {
|
|
387
|
+
case 'request':
|
|
388
|
+
console.log('New request:', payload);
|
|
389
|
+
break;
|
|
390
|
+
case 'exception':
|
|
391
|
+
console.log('New exception:', payload);
|
|
392
|
+
break;
|
|
393
|
+
case 'log':
|
|
394
|
+
console.log('New log:', payload);
|
|
395
|
+
break;
|
|
396
|
+
case 'connected':
|
|
397
|
+
console.log('Connected, clients:', payload.clientCount);
|
|
398
|
+
break;
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
## Types
|
|
404
|
+
|
|
405
|
+
### RequestEntry
|
|
406
|
+
|
|
407
|
+
```typescript
|
|
408
|
+
interface RequestEntry {
|
|
409
|
+
id: string;
|
|
410
|
+
method: string;
|
|
411
|
+
path: string;
|
|
412
|
+
url: string;
|
|
413
|
+
headers: Record<string, string>;
|
|
414
|
+
body?: unknown;
|
|
415
|
+
query?: Record<string, string>;
|
|
416
|
+
status: number;
|
|
417
|
+
responseHeaders: Record<string, string>;
|
|
418
|
+
responseBody?: unknown;
|
|
419
|
+
duration: number;
|
|
420
|
+
timestamp: Date;
|
|
421
|
+
ip?: string;
|
|
422
|
+
userId?: string;
|
|
423
|
+
tags?: string[];
|
|
424
|
+
}
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
### ExceptionEntry
|
|
428
|
+
|
|
429
|
+
```typescript
|
|
430
|
+
interface ExceptionEntry {
|
|
431
|
+
id: string;
|
|
432
|
+
name: string;
|
|
433
|
+
message: string;
|
|
434
|
+
stack: StackFrame[];
|
|
435
|
+
source?: SourceContext;
|
|
436
|
+
requestId?: string;
|
|
437
|
+
timestamp: Date;
|
|
438
|
+
handled: boolean;
|
|
439
|
+
tags?: string[];
|
|
440
|
+
}
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
### LogEntry
|
|
444
|
+
|
|
445
|
+
```typescript
|
|
446
|
+
interface LogEntry {
|
|
447
|
+
id: string;
|
|
448
|
+
level: 'debug' | 'info' | 'warn' | 'error';
|
|
449
|
+
message: string;
|
|
450
|
+
context?: Record<string, unknown>;
|
|
451
|
+
requestId?: string;
|
|
452
|
+
timestamp: Date;
|
|
453
|
+
}
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
## Integration with gkm dev
|
|
457
|
+
|
|
458
|
+
When using `gkm dev`, Telescope is automatically integrated:
|
|
459
|
+
|
|
460
|
+
```bash
|
|
461
|
+
gkm dev --port 3000
|
|
462
|
+
|
|
463
|
+
# Output:
|
|
464
|
+
# API: http://localhost:3000
|
|
465
|
+
# Telescope: http://localhost:3000/__telescope
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
Configure in `gkm.config.ts`:
|
|
469
|
+
|
|
470
|
+
```typescript
|
|
471
|
+
export default {
|
|
472
|
+
routes: './src/endpoints/**/*.ts',
|
|
473
|
+
envParser: './src/config/env',
|
|
474
|
+
logger: './src/logger',
|
|
475
|
+
|
|
476
|
+
dev: {
|
|
477
|
+
telescope: {
|
|
478
|
+
enabled: true,
|
|
479
|
+
path: '/__telescope',
|
|
480
|
+
},
|
|
481
|
+
},
|
|
482
|
+
};
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
## Best Practices
|
|
486
|
+
|
|
487
|
+
1. **Disable in Production**: Use environment variables to disable Telescope in production
|
|
488
|
+
```typescript
|
|
489
|
+
const telescope = new Telescope({
|
|
490
|
+
storage: new InMemoryStorage(),
|
|
491
|
+
enabled: process.env.NODE_ENV === 'development',
|
|
492
|
+
});
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
2. **Ignore Health Checks**: Exclude noisy endpoints
|
|
496
|
+
```typescript
|
|
497
|
+
ignorePatterns: ['/health', '/ready', '/metrics']
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
3. **Limit Body Size**: Prevent memory issues with large payloads
|
|
501
|
+
```typescript
|
|
502
|
+
maxBodySize: 32 * 1024 // 32KB
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
4. **Auto-Prune**: Enable automatic cleanup for long-running dev sessions
|
|
506
|
+
```typescript
|
|
507
|
+
pruneAfterHours: 4
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
5. **Use Database Storage in Staging**: For debugging issues in staging environments
|
|
511
|
+
```typescript
|
|
512
|
+
const storage = process.env.NODE_ENV === 'production'
|
|
513
|
+
? null // Disabled
|
|
514
|
+
: process.env.NODE_ENV === 'staging'
|
|
515
|
+
? new KyselyStorage(db)
|
|
516
|
+
: new InMemoryStorage();
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
## License
|
|
520
|
+
|
|
521
|
+
MIT
|