@lssm/lib.logger 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +550 -0
  2. package/dist/_virtual/rolldown_runtime.cjs +1 -0
  3. package/dist/context.cjs +1 -0
  4. package/dist/context.d.cts +44 -0
  5. package/dist/context.d.cts.map +1 -0
  6. package/dist/context.d.ts +44 -0
  7. package/dist/context.d.ts.map +1 -0
  8. package/dist/context.js +2 -0
  9. package/dist/context.js.map +1 -0
  10. package/dist/elysia-plugin.cjs +1 -0
  11. package/dist/elysia-plugin.d.cts +64 -0
  12. package/dist/elysia-plugin.d.cts.map +1 -0
  13. package/dist/elysia-plugin.d.ts +64 -0
  14. package/dist/elysia-plugin.d.ts.map +1 -0
  15. package/dist/elysia-plugin.js +2 -0
  16. package/dist/elysia-plugin.js.map +1 -0
  17. package/dist/formatters.cjs +9 -0
  18. package/dist/formatters.d.cts +29 -0
  19. package/dist/formatters.d.cts.map +1 -0
  20. package/dist/formatters.d.ts +29 -0
  21. package/dist/formatters.d.ts.map +1 -0
  22. package/dist/formatters.js +10 -0
  23. package/dist/formatters.js.map +1 -0
  24. package/dist/index.cjs +1 -0
  25. package/dist/index.d.cts +8 -0
  26. package/dist/index.d.ts +8 -0
  27. package/dist/index.js +1 -0
  28. package/dist/logger.cjs +1 -0
  29. package/dist/logger.d.cts +51 -0
  30. package/dist/logger.d.cts.map +1 -0
  31. package/dist/logger.d.ts +51 -0
  32. package/dist/logger.d.ts.map +1 -0
  33. package/dist/logger.js +2 -0
  34. package/dist/logger.js.map +1 -0
  35. package/dist/timer.cjs +1 -0
  36. package/dist/timer.d.cts +103 -0
  37. package/dist/timer.d.cts.map +1 -0
  38. package/dist/timer.d.ts +103 -0
  39. package/dist/timer.d.ts.map +1 -0
  40. package/dist/timer.js +2 -0
  41. package/dist/timer.js.map +1 -0
  42. package/dist/tracer.cjs +1 -0
  43. package/dist/tracer.d.cts +51 -0
  44. package/dist/tracer.d.cts.map +1 -0
  45. package/dist/tracer.d.ts +51 -0
  46. package/dist/tracer.d.ts.map +1 -0
  47. package/dist/tracer.js +2 -0
  48. package/dist/tracer.js.map +1 -0
  49. package/dist/types.cjs +1 -0
  50. package/dist/types.d.cts +71 -0
  51. package/dist/types.d.cts.map +1 -0
  52. package/dist/types.d.ts +71 -0
  53. package/dist/types.d.ts.map +1 -0
  54. package/dist/types.js +2 -0
  55. package/dist/types.js.map +1 -0
  56. package/package.json +98 -0
package/README.md ADDED
@@ -0,0 +1,550 @@
1
+ # @lssm/lib.logger
2
+
3
+ This library builds with tsdown using the shared `@lssm/tool.tsdown` presets.
4
+
5
+ A comprehensive logging library optimized for Bun with **first-class ElysiaJS integration**, distributed tracing, context management, and beautiful development output.
6
+
7
+ ## Features
8
+
9
+ - 🚀 **Optimized for Bun** - Uses Bun's native performance APIs and optimized for Bun runtime
10
+ - 🦋 **ElysiaJS Native** - Seamless integration with [ElysiaJS](https://elysiajs.com) lifecycle and context
11
+ - 🔍 **Distributed Tracing** - Track operations across HTTP, WebSocket, cron, and queue processing
12
+ - ⏱️ **Performance Timing** - Built-in high-precision timing for operations
13
+ - 🔄 **Context Management** - Maintain context across async operations (like cls-hooked/nestjs-cls)
14
+ - 🎨 **Environment-aware Output** - Pretty logs in dev, structured JSON in production
15
+ - 🔌 **Easy Integrations** - Built-in middleware for HTTP, WebSocket, cron, and queues
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ bun add @lssm/lib.logger
21
+ # If using ElysiaJS
22
+ bun add elysia @lssm/lib.logger
23
+ ```
24
+
25
+ ## Quick Start
26
+
27
+ ```typescript
28
+ import { logger, Logger, LogLevel } from "@lssm/lib.logger";
29
+
30
+ // Basic logging
31
+ logger.info("Server started", { port: 3000 });
32
+ logger.error("Database error", { table: "users" }, error);
33
+
34
+ // Create a custom logger
35
+ const customLogger = new Logger({
36
+ level: LogLevel.DEBUG,
37
+ environment: "production",
38
+ enableTracing: true,
39
+ });
40
+ ```
41
+
42
+ ## Core Logging
43
+
44
+ ```typescript
45
+ import { logger } from "@lssm/lib.logger";
46
+
47
+ // Different log levels
48
+ logger.trace("Detailed debug info"); // TRACE level
49
+ logger.debug("Debug information"); // DEBUG level
50
+ logger.info("General information"); // INFO level
51
+ logger.warn("Warning message"); // WARN level
52
+ logger.error("Error occurred", {}, err); // ERROR level
53
+ logger.fatal("Critical error", {}, err); // FATAL level
54
+ ```
55
+
56
+ ## Context Management
57
+
58
+ The logger maintains context across async operations using AsyncLocalStorage:
59
+
60
+ ```typescript
61
+ import { logger } from "@lssm/lib.logger";
62
+
63
+ // Run code within a context
64
+ logger.withContext({ userId: "123", requestId: "abc" }, () => {
65
+ logger.info("Processing user request"); // Will include userId and requestId
66
+
67
+ someAsyncOperation(); // Context is maintained across async calls
68
+ });
69
+
70
+ // Extend existing context
71
+ logger.extendContext({ operation: "payment" }, () => {
72
+ logger.info("Processing payment"); // Includes all previous context + operation
73
+ });
74
+
75
+ // Set individual context values
76
+ logger.setContext("sessionId", "xyz789");
77
+ logger.info("User action"); // Will include sessionId
78
+
79
+ // Get current context
80
+ const context = logger.getContext();
81
+ console.log(context); // { userId: '123', requestId: 'abc', sessionId: 'xyz789' }
82
+ ```
83
+
84
+ ## Distributed Tracing
85
+
86
+ Track operations across your application with automatic span management:
87
+
88
+ ```typescript
89
+ import { logger } from "@lssm/lib.logger";
90
+
91
+ // Trace an operation
92
+ await logger.trace(
93
+ {
94
+ operationType: "http",
95
+ operationName: "GET /api/users",
96
+ metadata: { userId: "123" },
97
+ tags: ["api", "users"],
98
+ autoTiming: true,
99
+ },
100
+ async () => {
101
+ // Your operation code here
102
+ const users = await fetchUsers();
103
+ return users;
104
+ }
105
+ );
106
+
107
+ // Manual span management
108
+ const span = logger.startSpan({
109
+ operationType: "database",
110
+ operationName: "user-query",
111
+ });
112
+
113
+ try {
114
+ const result = await database.query("SELECT * FROM users");
115
+ logger.addTraceMetadata("rowCount", result.length);
116
+ logger.addTraceTags("database", "users");
117
+ return result;
118
+ } finally {
119
+ logger.finishSpan(span.spanId);
120
+ }
121
+ ```
122
+
123
+ ## Performance Timing
124
+
125
+ Measure execution times with high precision:
126
+
127
+ ```typescript
128
+ import { logger } from "@lssm/lib.logger";
129
+
130
+ // Start a timer
131
+ const timer = logger.startTimer("database-operation");
132
+
133
+ // ... do some work ...
134
+
135
+ const duration = timer.stop();
136
+ console.log(`Operation took ${duration}ms`);
137
+
138
+ // Profile function execution
139
+ const result = await logger.profile(
140
+ "expensive-calculation",
141
+ async () => {
142
+ return await someExpensiveOperation();
143
+ },
144
+ { logResult: true, logLevel: LogLevel.INFO }
145
+ );
146
+
147
+ // Manual timing with laps
148
+ const timer2 = logger.startTimer("multi-step-process");
149
+ timer2.lap("step-1-complete");
150
+ // ... more work ...
151
+ timer2.lap("step-2-complete");
152
+ const total = timer2.stop();
153
+ ```
154
+
155
+ ## ElysiaJS Integration
156
+
157
+ ### Basic Setup
158
+
159
+ ```typescript
160
+ import { Elysia } from "elysia";
161
+ import { elysiaLogger } from "@lssm/lib.logger";
162
+
163
+ const app = new Elysia()
164
+ .use(
165
+ elysiaLogger({
166
+ logRequests: true,
167
+ logResponses: true,
168
+ excludePaths: ["/health", "/metrics"],
169
+ })
170
+ )
171
+ .get("/", ({ logInfo }) => {
172
+ logInfo("Processing request");
173
+ return "Hello World";
174
+ })
175
+ .listen(3000);
176
+ ```
177
+
178
+ The plugin automatically:
179
+
180
+ - ✅ Logs all incoming requests with timing
181
+ - ✅ Logs responses with status codes and duration
182
+ - ✅ Traces operations with correlation IDs
183
+ - ✅ Maintains context across async operations
184
+ - ✅ Provides helper functions in route context
185
+
186
+ ### Advanced ElysiaJS Usage
187
+
188
+ ```typescript
189
+ import { Elysia } from "elysia";
190
+ import {
191
+ elysiaLogger,
192
+ createDatabaseUtils,
193
+ Logger,
194
+ LogLevel,
195
+ } from "@lssm/lib.logger";
196
+
197
+ const customLogger = new Logger({
198
+ level: LogLevel.DEBUG,
199
+ environment: "production",
200
+ });
201
+
202
+ const db = createDatabaseUtils(customLogger);
203
+
204
+ const app = new Elysia()
205
+ .use(
206
+ elysiaLogger({
207
+ logger: customLogger,
208
+ maskSensitiveData: true,
209
+ })
210
+ )
211
+ .derive(() => ({ db }))
212
+ .get("/users", async ({ logInfo, traceOperation, db }) => {
213
+ logInfo("Fetching users from database");
214
+
215
+ const users = await traceOperation("fetch-users", async () => {
216
+ return db.query("select-users", () =>
217
+ // Your database query here
218
+ Promise.resolve([{ id: 1, name: "John" }])
219
+ );
220
+ });
221
+
222
+ return users;
223
+ })
224
+ .listen(3000);
225
+ ```
226
+
227
+ ### ElysiaJS WebSocket Logging
228
+
229
+ ```typescript
230
+ import { Elysia } from "elysia";
231
+ import { elysiaLogger, createWebSocketUtils } from "@lssm/lib.logger";
232
+
233
+ const wsLogger = createWebSocketUtils();
234
+
235
+ const app = new Elysia()
236
+ .use(elysiaLogger())
237
+ .ws("/chat", {
238
+ open(ws) {
239
+ const connectionId = crypto.randomUUID();
240
+ wsLogger.logConnection(connectionId, {
241
+ userAgent: ws.data.headers?.["user-agent"],
242
+ });
243
+ },
244
+ message(ws, message) {
245
+ wsLogger.logMessage(ws.id, typeof message, message.length);
246
+ ws.send(`Echo: ${message}`);
247
+ },
248
+ close(ws, code, reason) {
249
+ wsLogger.logDisconnection(ws.id, code, reason);
250
+ },
251
+ })
252
+ .listen(3000);
253
+ ```
254
+
255
+ ### ElysiaJS with Authentication Logging
256
+
257
+ ```typescript
258
+ import { Elysia } from "elysia";
259
+ import { elysiaLogger, createAuthUtils } from "@lssm/lib.logger";
260
+
261
+ const authLogger = createAuthUtils();
262
+
263
+ const app = new Elysia()
264
+ .use(elysiaLogger())
265
+ .derive(() => ({ authLogger }))
266
+ .post("/auth/login", async ({ body, authLogger }) => {
267
+ try {
268
+ // Your authentication logic
269
+ const user = await authenticateUser(body.email, body.password);
270
+
271
+ authLogger.logLogin(user.id, {
272
+ email: body.email,
273
+ ip: request.headers.get("x-forwarded-for"),
274
+ });
275
+
276
+ return { success: true, token: generateToken(user) };
277
+ } catch (error) {
278
+ authLogger.logAuthFailure("invalid_credentials", {
279
+ email: body.email,
280
+ });
281
+ throw error;
282
+ }
283
+ })
284
+ .listen(3000);
285
+ ```
286
+
287
+ ### Production Configuration for ElysiaJS
288
+
289
+ ```typescript
290
+ import { Elysia } from "elysia";
291
+ import { elysiaLogger, Logger, LogLevel } from "@lssm/lib.logger";
292
+
293
+ const productionLogger = new Logger({
294
+ level: LogLevel.INFO,
295
+ environment: "production",
296
+ enableTracing: true,
297
+ enableTiming: true,
298
+ });
299
+
300
+ const app = new Elysia()
301
+ .use(
302
+ elysiaLogger({
303
+ logger: productionLogger,
304
+ logRequests: true,
305
+ logResponses: true,
306
+ excludePaths: ["/health", "/metrics", "/favicon.ico"],
307
+ maskSensitiveData: true,
308
+ })
309
+ )
310
+ .get("/health", () => ({ status: "ok" })) // This won't be logged
311
+ .get("/api/*", ({ logInfo, traceOperation }) => {
312
+ // All API routes automatically logged and traced
313
+ return traceOperation("business-logic", async () => {
314
+ // Your business logic here
315
+ return { success: true };
316
+ });
317
+ })
318
+ .listen(3000);
319
+ ```
320
+
321
+ ## HTTP Integration (Non-ElysiaJS)
322
+
323
+ ### Express/Hono Middleware
324
+
325
+ ```typescript
326
+ import { createHttpMiddleware } from "@lssm/lib.logger";
327
+ import express from "express";
328
+
329
+ const app = express();
330
+
331
+ // Add logging middleware
332
+ app.use(createHttpMiddleware()); // Uses default logger
333
+
334
+ // All subsequent requests will have context and tracing
335
+ app.get("/api/users", (req, res) => {
336
+ logger.info("Fetching users"); // Automatically includes request context
337
+ // ... handle request
338
+ });
339
+ ```
340
+
341
+ ### Bun HTTP Server
342
+
343
+ ```typescript
344
+ import { createBunHttpHandler } from "@lssm/lib.logger";
345
+
346
+ const handler = createBunHttpHandler(async (req) => {
347
+ // Request is automatically traced and timed
348
+ logger.info("Processing request"); // Includes request context
349
+
350
+ return new Response("Hello World");
351
+ });
352
+
353
+ Bun.serve({
354
+ fetch: handler,
355
+ port: 3000,
356
+ });
357
+ ```
358
+
359
+ ## WebSocket Integration
360
+
361
+ ```typescript
362
+ import { createWebSocketMiddleware } from "@lssm/lib.logger";
363
+
364
+ const wsMiddleware = createWebSocketMiddleware();
365
+
366
+ // WebSocket server setup
367
+ const server = Bun.serve({
368
+ websocket: {
369
+ open: wsMiddleware.onOpen,
370
+ message: wsMiddleware.onMessage,
371
+ close: wsMiddleware.onClose,
372
+ error: wsMiddleware.onError,
373
+ },
374
+ });
375
+ ```
376
+
377
+ ## Cron Job Integration
378
+
379
+ ```typescript
380
+ import { createCronWrapper } from "@lssm/lib.logger";
381
+
382
+ const cronWrapper = createCronWrapper();
383
+
384
+ // Wrap your cron jobs
385
+ const wrappedJob = cronWrapper("daily-cleanup", async () => {
386
+ logger.info("Starting daily cleanup");
387
+ await cleanupOldData();
388
+ logger.info("Cleanup completed");
389
+ });
390
+
391
+ // Schedule with your preferred cron library
392
+ schedule.scheduleJob("0 2 * * *", wrappedJob);
393
+ ```
394
+
395
+ ## Queue Processing Integration
396
+
397
+ ```typescript
398
+ import { createQueueWrapper } from "@lssm/lib.logger";
399
+
400
+ const queueWrapper = createQueueWrapper();
401
+
402
+ // Wrap queue processors
403
+ const processEmailJob = queueWrapper.wrapProcessor(
404
+ "email-queue",
405
+ async (job: EmailJob, jobId: string) => {
406
+ logger.info("Processing email job", { recipient: job.recipient });
407
+ await sendEmail(job);
408
+ return { success: true };
409
+ }
410
+ );
411
+
412
+ // Log queue events
413
+ queueWrapper.logEnqueue("email-queue", "job-123", emailData);
414
+ queueWrapper.logDequeue("email-queue", "job-123");
415
+ queueWrapper.logRetry("email-queue", "job-123", 2, 5);
416
+ ```
417
+
418
+ ## Database Integration
419
+
420
+ ```typescript
421
+ import { createDatabaseWrapper } from "@lssm/lib.logger";
422
+
423
+ const dbWrapper = createDatabaseWrapper();
424
+
425
+ // Wrap database operations
426
+ const users = await dbWrapper.wrapQuery(
427
+ "fetch-users",
428
+ () => prisma.user.findMany(),
429
+ { table: "users", operation: "findMany" }
430
+ );
431
+ ```
432
+
433
+ ## Configuration
434
+
435
+ ```typescript
436
+ import { Logger, LogLevel } from "@lssm/lib.logger";
437
+
438
+ const logger = new Logger({
439
+ level: LogLevel.INFO, // Minimum log level
440
+ environment: "production", // 'development' | 'production' | 'test'
441
+ enableTracing: true, // Enable distributed tracing
442
+ enableTiming: true, // Enable performance timing
443
+ enableContext: true, // Enable context management
444
+ enableColors: true, // Enable colors in dev output
445
+ maxContextDepth: 10, // Max nested context depth
446
+ timestampFormat: "iso", // 'iso' | 'epoch' | 'relative'
447
+ });
448
+ ```
449
+
450
+ ## Custom Formatters
451
+
452
+ ```typescript
453
+ import {
454
+ Logger,
455
+ CustomFormatter,
456
+ DevFormatter,
457
+ ProductionFormatter,
458
+ } from "@lssm/lib.logger";
459
+
460
+ // Use custom formatter
461
+ const customFormatter = new CustomFormatter(
462
+ "{timestamp} [{level}] {traceId} {message}",
463
+ (date) => date.toLocaleDateString()
464
+ );
465
+
466
+ const logger = new Logger();
467
+ logger.setFormatter(customFormatter);
468
+
469
+ // Or create your own
470
+ class MyFormatter implements Formatter {
471
+ format(entry: LogEntry): string {
472
+ return `${entry.timestamp} - ${entry.message}`;
473
+ }
474
+ }
475
+ ```
476
+
477
+ ## Example Output
478
+
479
+ ### Development Mode (Pretty)
480
+
481
+ ```
482
+ 14:32:15.123 ● INFO [trace:a1b2c3d4|span:e5f6g7h8] HTTP request started (12.34ms)
483
+ Context: { userId: "123", requestId: "req-456" }
484
+ Metadata: { method: "GET", url: "/api/users", userAgent: "Mozilla/5.0..." }
485
+
486
+ 14:32:15.135 ✖ ERROR Database connection failed (156.78ms)
487
+ Error: ConnectionError: Unable to connect to database
488
+ at DatabaseClient.connect (database.js:45:12)
489
+ at UserService.fetchUsers (user-service.js:23:8)
490
+ at ApiHandler.getUsers (api-handler.js:67:15)
491
+ ```
492
+
493
+ ### Production Mode (JSON)
494
+
495
+ ```json
496
+ {"timestamp":"2024-01-15T14:32:15.123Z","level":"info","message":"HTTP request started","traceId":"a1b2c3d4e5f6g7h8","spanId":"e5f6g7h8","duration":12.34,"context":{"userId":"123","requestId":"req-456"},"metadata":{"method":"GET","url":"/api/users"}}
497
+
498
+ {"timestamp":"2024-01-15T14:32:15.135Z","level":"error","message":"Database connection failed","traceId":"a1b2c3d4e5f6g7h8","duration":156.78,"error":{"name":"ConnectionError","message":"Unable to connect to database","stack":"ConnectionError: Unable to connect to database\n at DatabaseClient.connect..."}}
499
+ ```
500
+
501
+ ## Best Practices
502
+
503
+ 1. **Use context for request correlation**:
504
+
505
+ ```typescript
506
+ logger.withContext({ requestId, userId }, () => {
507
+ // All logs in this scope will include requestId and userId
508
+ });
509
+ ```
510
+
511
+ 2. **Trace important operations**:
512
+
513
+ ```typescript
514
+ await logger.trace(
515
+ {
516
+ operationType: "database",
517
+ operationName: "user-creation",
518
+ },
519
+ () => createUser(userData)
520
+ );
521
+ ```
522
+
523
+ 3. **Profile performance-critical code**:
524
+
525
+ ```typescript
526
+ const result = await logger.profile("data-processing", () => {
527
+ return processLargeDataset(data);
528
+ });
529
+ ```
530
+
531
+ 4. **Use appropriate log levels**:
532
+
533
+ - `trace`: Very detailed debugging (typically disabled in production)
534
+ - `debug`: Debugging information
535
+ - `info`: General application flow
536
+ - `warn`: Warning conditions
537
+ - `error`: Error conditions
538
+ - `fatal`: Critical errors that might cause the application to exit
539
+
540
+ 5. **Clean up in production**:
541
+ ```typescript
542
+ process.on("SIGTERM", async () => {
543
+ await logger.flush();
544
+ process.exit(0);
545
+ });
546
+ ```
547
+
548
+ ## License
549
+
550
+ MIT
@@ -0,0 +1 @@
1
+ var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));exports.__toESM=s;
@@ -0,0 +1 @@
1
+ const e=require(`./_virtual/rolldown_runtime.cjs`);let t=require(`node:async_hooks`);t=e.__toESM(t);var n=class e{static instance;storage;constructor(){this.storage=new t.AsyncLocalStorage}static getInstance(){return e.instance||=new e,e.instance}run(e,t){let n={context:{...e},trace:this.getCurrentTrace()};return this.storage.run(n,t)}extend(e,t){let n={...this.getContext(),...e};return this.run(n,t)}set(e,t){let n=this.storage.getStore();n&&(n.context[e]=t)}get(e){return this.storage.getStore()?.context?.[e]}getContext(){return this.storage.getStore()?.context||{}}setTrace(e){let t=this.storage.getStore();t&&(t.trace=e)}getCurrentTrace(){return this.storage.getStore()?.trace}generateId(){return crypto.randomUUID()}};exports.LogContext=n;
@@ -0,0 +1,44 @@
1
+ import { ContextData, TraceContext } from "./types.cjs";
2
+
3
+ //#region src/context.d.ts
4
+ declare class LogContext {
5
+ private static instance;
6
+ private storage;
7
+ constructor();
8
+ static getInstance(): LogContext;
9
+ /**
10
+ * Run a function with a new context
11
+ */
12
+ run<T>(context: ContextData, fn: () => T): T;
13
+ /**
14
+ * Run a function with an extended context (merges with current)
15
+ */
16
+ extend<T>(additionalContext: Partial<ContextData>, fn: () => T): T;
17
+ /**
18
+ * Set context data for the current execution context
19
+ */
20
+ set(key: string, value: any): void;
21
+ /**
22
+ * Get a specific context value
23
+ */
24
+ get<T>(key: string): T | undefined;
25
+ /**
26
+ * Get all context data
27
+ */
28
+ getContext(): ContextData;
29
+ /**
30
+ * Set trace context
31
+ */
32
+ setTrace(trace: TraceContext): void;
33
+ /**
34
+ * Get current trace context
35
+ */
36
+ getCurrentTrace(): TraceContext | undefined;
37
+ /**
38
+ * Generate a unique ID for requests/operations
39
+ */
40
+ generateId(): string;
41
+ }
42
+ //#endregion
43
+ export { LogContext };
44
+ //# sourceMappingURL=context.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.cts","names":[],"sources":["../src/context.ts"],"sourcesContent":[],"mappings":";;;cAQa,UAAA;;EAAA,QAAA,OAAU;EAQC,WAAA,CAAA;EAUN,OAAA,WAAA,CAAA,CAAA,EAVM,UAUN;EAAuB;;;EAWV,GAAA,CAAA,CAAA,CAAA,CAAA,OAAA,EAXb,WAWa,EAAA,EAAA,EAAA,GAAA,GAXU,CAWV,CAAA,EAXc,CAWd;EAAgC;;;EA+B/C,MAAA,CAAA,CAAA,CAAA,CAAA,iBAAA,EA/Be,OA+Bf,CA/BuB,WA+BvB,CAAA,EAAA,EAAA,EAAA,GAAA,GA/B+C,CA+B/C,CAAA,EA/BmD,CA+BnD;EAQE;;;;;;;uBAhBK;;;;gBAQP;;;;kBAQE;;;;qBAUG"}
@@ -0,0 +1,44 @@
1
+ import { ContextData, TraceContext } from "./types.js";
2
+
3
+ //#region src/context.d.ts
4
+ declare class LogContext {
5
+ private static instance;
6
+ private storage;
7
+ constructor();
8
+ static getInstance(): LogContext;
9
+ /**
10
+ * Run a function with a new context
11
+ */
12
+ run<T>(context: ContextData, fn: () => T): T;
13
+ /**
14
+ * Run a function with an extended context (merges with current)
15
+ */
16
+ extend<T>(additionalContext: Partial<ContextData>, fn: () => T): T;
17
+ /**
18
+ * Set context data for the current execution context
19
+ */
20
+ set(key: string, value: any): void;
21
+ /**
22
+ * Get a specific context value
23
+ */
24
+ get<T>(key: string): T | undefined;
25
+ /**
26
+ * Get all context data
27
+ */
28
+ getContext(): ContextData;
29
+ /**
30
+ * Set trace context
31
+ */
32
+ setTrace(trace: TraceContext): void;
33
+ /**
34
+ * Get current trace context
35
+ */
36
+ getCurrentTrace(): TraceContext | undefined;
37
+ /**
38
+ * Generate a unique ID for requests/operations
39
+ */
40
+ generateId(): string;
41
+ }
42
+ //#endregion
43
+ export { LogContext };
44
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","names":[],"sources":["../src/context.ts"],"sourcesContent":[],"mappings":";;;cAQa,UAAA;;EAAA,QAAA,OAAU;EAQC,WAAA,CAAA;EAUN,OAAA,WAAA,CAAA,CAAA,EAVM,UAUN;EAAuB;;;EAWV,GAAA,CAAA,CAAA,CAAA,CAAA,OAAA,EAXb,WAWa,EAAA,EAAA,EAAA,GAAA,GAXU,CAWV,CAAA,EAXc,CAWd;EAAgC;;;EA+B/C,MAAA,CAAA,CAAA,CAAA,CAAA,iBAAA,EA/Be,OA+Bf,CA/BuB,WA+BvB,CAAA,EAAA,EAAA,EAAA,GAAA,GA/B+C,CA+B/C,CAAA,EA/BmD,CA+BnD;EAQE;;;;;;;uBAhBK;;;;gBAQP;;;;kBAQE;;;;qBAUG"}
@@ -0,0 +1,2 @@
1
+ import{AsyncLocalStorage as e}from"node:async_hooks";var t=class t{static instance;storage;constructor(){this.storage=new e}static getInstance(){return t.instance||=new t,t.instance}run(e,t){let n={context:{...e},trace:this.getCurrentTrace()};return this.storage.run(n,t)}extend(e,t){let n={...this.getContext(),...e};return this.run(n,t)}set(e,t){let n=this.storage.getStore();n&&(n.context[e]=t)}get(e){return this.storage.getStore()?.context?.[e]}getContext(){return this.storage.getStore()?.context||{}}setTrace(e){let t=this.storage.getStore();t&&(t.trace=e)}getCurrentTrace(){return this.storage.getStore()?.trace}generateId(){return crypto.randomUUID()}};export{t as LogContext};
2
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","names":["contextData: LogContextData","mergedContext: ContextData"],"sources":["../src/context.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks';\nimport type { ContextData, TraceContext } from './types';\n\ninterface LogContextData {\n context: ContextData;\n trace?: TraceContext;\n}\n\nexport class LogContext {\n private static instance: LogContext;\n private storage: AsyncLocalStorage<LogContextData>;\n\n constructor() {\n this.storage = new AsyncLocalStorage<LogContextData>();\n }\n\n static getInstance(): LogContext {\n if (!LogContext.instance) {\n LogContext.instance = new LogContext();\n }\n return LogContext.instance;\n }\n\n /**\n * Run a function with a new context\n */\n run<T>(context: ContextData, fn: () => T): T {\n const contextData: LogContextData = {\n context: { ...context },\n trace: this.getCurrentTrace(),\n };\n return this.storage.run(contextData, fn) as T;\n }\n\n /**\n * Run a function with an extended context (merges with current)\n */\n extend<T>(additionalContext: Partial<ContextData>, fn: () => T): T {\n const currentContext = this.getContext();\n const mergedContext: ContextData = {\n ...currentContext,\n ...additionalContext,\n };\n return this.run(mergedContext, fn);\n }\n\n /**\n * Set context data for the current execution context\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n set(key: string, value: any): void {\n const current = this.storage.getStore();\n if (current) {\n current.context[key] = value;\n }\n }\n\n /**\n * Get a specific context value\n */\n get<T>(key: string): T | undefined {\n const current = this.storage.getStore();\n return current?.context?.[key];\n }\n\n /**\n * Get all context data\n */\n getContext(): ContextData {\n const current = this.storage.getStore();\n return current?.context || {};\n }\n\n /**\n * Set trace context\n */\n setTrace(trace: TraceContext): void {\n const current = this.storage.getStore();\n if (current) {\n current.trace = trace;\n }\n }\n\n /**\n * Get current trace context\n */\n getCurrentTrace(): TraceContext | undefined {\n const current = this.storage.getStore();\n return current?.trace;\n }\n /**\n * Generate a unique ID for requests/operations\n */\n generateId(): string {\n return crypto.randomUUID();\n }\n}\n"],"mappings":"qDAQA,IAAa,EAAb,MAAa,CAAW,CACtB,OAAe,SACf,QAEA,aAAc,CACZ,KAAK,QAAU,IAAI,EAGrB,OAAO,aAA0B,CAI/B,MAHA,CACE,EAAW,WAAW,IAAI,EAErB,EAAW,SAMpB,IAAO,EAAsB,EAAgB,CAC3C,IAAMA,EAA8B,CAClC,QAAS,CAAE,GAAG,EAAS,CACvB,MAAO,KAAK,iBAAiB,CAC9B,CACD,OAAO,KAAK,QAAQ,IAAI,EAAa,EAAG,CAM1C,OAAU,EAAyC,EAAgB,CAEjE,IAAMC,EAA6B,CACjC,GAFqB,KAAK,YAAY,CAGtC,GAAG,EACJ,CACD,OAAO,KAAK,IAAI,EAAe,EAAG,CAOpC,IAAI,EAAa,EAAkB,CACjC,IAAM,EAAU,KAAK,QAAQ,UAAU,CACnC,IACF,EAAQ,QAAQ,GAAO,GAO3B,IAAO,EAA4B,CAEjC,OADgB,KAAK,QAAQ,UAAU,EACvB,UAAU,GAM5B,YAA0B,CAExB,OADgB,KAAK,QAAQ,UAAU,EACvB,SAAW,EAAE,CAM/B,SAAS,EAA2B,CAClC,IAAM,EAAU,KAAK,QAAQ,UAAU,CACnC,IACF,EAAQ,MAAQ,GAOpB,iBAA4C,CAE1C,OADgB,KAAK,QAAQ,UAAU,EACvB,MAKlB,YAAqB,CACnB,OAAO,OAAO,YAAY"}
@@ -0,0 +1 @@
1
+ const e=require(`./_virtual/rolldown_runtime.cjs`),t=require(`./context.cjs`),n=require(`./logger.cjs`);require(`elysia`);function r(e={}){let{logger:r=new n.Logger,logRequests:i=!0,logResponses:a=!0,excludePaths:o=[`/health`,`/metrics`]}=e,s=t.LogContext.getInstance();return function(e){return e.derive(e=>{let{request:t,path:n}=e;if(o.some(e=>n.startsWith(e)))return{logger:r};let a=new URL(t.url),c={requestId:s.generateId(),method:t.method,url:t.url,path:a.pathname,userAgent:t.headers.get(`user-agent`)||void 0,timestamp:new Date().toISOString()},l=performance.now();return s.run(c,()=>{i&&r.info(`→ ${t.method} ${n}`,{method:t.method,path:n,userAgent:c.userAgent,requestId:c.requestId})}),{logger:r,requestContext:c,startTime:l}}).onAfterHandle(e=>{let{request:t,startTime:n,requestContext:r,logger:i}=e;if(!n||!r)return;let o=performance.now()-n,s=new URL(t.url).pathname;a&&i.info(`← 200 ${t.method} ${s}`,{method:t.method,path:s,duration:`${o.toFixed(2)}ms`,requestId:r.requestId})}).onError(e=>{let{request:t,error:n,code:r,startTime:i,requestContext:a,logger:o}=e;if(!i||!a)return;let s=performance.now()-i,c=new URL(t.url).pathname;o?.error(`✖ ${r} ${t.method} ${c}`,{method:t.method,path:c,error:n?.toString?.()||`Unknown error`,code:r,duration:`${s.toFixed(2)}ms`,requestId:a.requestId})}).derive(()=>({logInfo:(e,t)=>{r.info(e,t)},logError:(e,t,n)=>{r.error(e,n,t)},traceOperation:async(e,t)=>r.trace({operationType:`custom`,operationName:e,autoTiming:!0},t)}))}}const i=r;exports.createElysiaLogger=i,exports.elysiaLogger=r;