observability-kit 0.1.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 (62) hide show
  1. package/README.md +661 -0
  2. package/dist/core/context/request-context.d.ts +9 -0
  3. package/dist/core/context/request-context.d.ts.map +1 -0
  4. package/dist/core/context/request-context.js +46 -0
  5. package/dist/core/context/request-context.js.map +1 -0
  6. package/dist/core/logger/adapters/adapter.interface.d.ts +5 -0
  7. package/dist/core/logger/adapters/adapter.interface.d.ts.map +1 -0
  8. package/dist/core/logger/adapters/adapter.interface.js +3 -0
  9. package/dist/core/logger/adapters/adapter.interface.js.map +1 -0
  10. package/dist/core/logger/adapters/node.adapter.d.ts +6 -0
  11. package/dist/core/logger/adapters/node.adapter.d.ts.map +1 -0
  12. package/dist/core/logger/adapters/node.adapter.js +12 -0
  13. package/dist/core/logger/adapters/node.adapter.js.map +1 -0
  14. package/dist/core/logger/adapters/pino.adapter.d.ts +18 -0
  15. package/dist/core/logger/adapters/pino.adapter.d.ts.map +1 -0
  16. package/dist/core/logger/adapters/pino.adapter.js +83 -0
  17. package/dist/core/logger/adapters/pino.adapter.js.map +1 -0
  18. package/dist/core/logger/structured-logger.d.ts +25 -0
  19. package/dist/core/logger/structured-logger.d.ts.map +1 -0
  20. package/dist/core/logger/structured-logger.js +47 -0
  21. package/dist/core/logger/structured-logger.js.map +1 -0
  22. package/dist/index.d.ts +8 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +17 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/nest/adapters/nest.adapter.d.ts +16 -0
  27. package/dist/nest/adapters/nest.adapter.d.ts.map +1 -0
  28. package/dist/nest/adapters/nest.adapter.js +58 -0
  29. package/dist/nest/adapters/nest.adapter.js.map +1 -0
  30. package/dist/nest/decorators/request-log.decorator.d.ts +3 -0
  31. package/dist/nest/decorators/request-log.decorator.d.ts.map +1 -0
  32. package/dist/nest/decorators/request-log.decorator.js +41 -0
  33. package/dist/nest/decorators/request-log.decorator.js.map +1 -0
  34. package/dist/nest/decorators/service-log.decorator.d.ts +3 -0
  35. package/dist/nest/decorators/service-log.decorator.d.ts.map +1 -0
  36. package/dist/nest/decorators/service-log.decorator.js +41 -0
  37. package/dist/nest/decorators/service-log.decorator.js.map +1 -0
  38. package/dist/nest/decorators/step-log.decorator.d.ts +3 -0
  39. package/dist/nest/decorators/step-log.decorator.d.ts.map +1 -0
  40. package/dist/nest/decorators/step-log.decorator.js +36 -0
  41. package/dist/nest/decorators/step-log.decorator.js.map +1 -0
  42. package/dist/nest/interceptors/request.interceptor.d.ts +11 -0
  43. package/dist/nest/interceptors/request.interceptor.d.ts.map +1 -0
  44. package/dist/nest/interceptors/request.interceptor.js +64 -0
  45. package/dist/nest/interceptors/request.interceptor.js.map +1 -0
  46. package/dist/nest/module/trace-logger.module.d.ts +8 -0
  47. package/dist/nest/module/trace-logger.module.d.ts.map +1 -0
  48. package/dist/nest/module/trace-logger.module.js +38 -0
  49. package/dist/nest/module/trace-logger.module.js.map +1 -0
  50. package/dist/nest/tokens.d.ts +2 -0
  51. package/dist/nest/tokens.d.ts.map +1 -0
  52. package/dist/nest/tokens.js +5 -0
  53. package/dist/nest/tokens.js.map +1 -0
  54. package/dist/nestjs.d.ts +8 -0
  55. package/dist/nestjs.d.ts.map +1 -0
  56. package/dist/nestjs.js +32 -0
  57. package/dist/nestjs.js.map +1 -0
  58. package/dist/types/options.d.ts +39 -0
  59. package/dist/types/options.d.ts.map +1 -0
  60. package/dist/types/options.js +3 -0
  61. package/dist/types/options.js.map +1 -0
  62. package/package.json +123 -0
package/README.md ADDED
@@ -0,0 +1,661 @@
1
+ # observability-kit
2
+
3
+ Structured logging + request tracing for **NestJS** and **plain Node.js**.
4
+
5
+ Automatic `requestId` correlation across controllers, services and internal steps — no manual ID passing.
6
+
7
+ ---
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npm install observability-kit
13
+ ```
14
+
15
+ Peer deps (NestJS only):
16
+
17
+ ```bash
18
+ npm install @nestjs/common @nestjs/core rxjs
19
+ ```
20
+
21
+ ---
22
+
23
+ ## Entry points
24
+
25
+ | Import | Contents |
26
+ |---|---|
27
+ | `observability-kit` | Core — context helpers + logger |
28
+ | `observability-kit/nestjs` | Core + NestJS module, decorators and interceptor |
29
+
30
+ ---
31
+
32
+ ## How it works
33
+
34
+ ```
35
+ HTTP Request
36
+
37
+ TraceInterceptor reads / generates requestId
38
+
39
+ AsyncLocalStorage context requestId lives here for the full async chain
40
+
41
+ @RequestLog emits request.start / request.end
42
+
43
+ @ServiceLog emits service.enter / service.exit
44
+
45
+ @StepLog emits step.<name>
46
+ ```
47
+
48
+ Every log line carries the same `requestId` automatically.
49
+
50
+ ---
51
+
52
+ ## NestJS setup
53
+
54
+ ### 1. Register the module
55
+
56
+ ```typescript
57
+ // app.module.ts
58
+ import { Module } from '@nestjs/common';
59
+ import { TraceLoggerModule } from 'observability-kit/nestjs';
60
+
61
+ @Module({
62
+ imports: [
63
+ TraceLoggerModule.forRoot({
64
+ serviceName: 'my-api',
65
+ requestIdHeaderName: 'x-request-id', // default
66
+ enableResponseHeader: true,
67
+ }),
68
+ ],
69
+ })
70
+ export class AppModule {}
71
+ ```
72
+
73
+ ### 2. Register the interceptor globally
74
+
75
+ ```typescript
76
+ // main.ts
77
+ import { NestFactory } from '@nestjs/core';
78
+ import { AppModule } from './app.module';
79
+ import { TraceInterceptor } from 'observability-kit/nestjs';
80
+ import { logger, PinoLogAdapter } from 'observability-kit';
81
+
82
+ async function bootstrap() {
83
+ logger.setAdapter(PinoLogAdapter.pretty()); // desarrollo — salida legible
84
+ // logger.setAdapter(new PinoLogAdapter()); // producción — JSON puro
85
+
86
+ const app = await NestFactory.create(AppModule);
87
+ app.useGlobalInterceptors(app.get(TraceInterceptor));
88
+ await app.listen(3000);
89
+ }
90
+ bootstrap();
91
+ ```
92
+
93
+ ### 3. Decorar controllers y services
94
+
95
+ ```typescript
96
+ import { Controller, Get, Param } from '@nestjs/common';
97
+ import { RequestLog, ServiceLog, StepLog } from 'observability-kit/nestjs';
98
+
99
+ @Controller('orders')
100
+ export class OrdersController {
101
+ constructor(private readonly ordersService: OrdersService) {}
102
+
103
+ @Get(':id')
104
+ @RequestLog()
105
+ findOne(@Param('id') id: string) {
106
+ return this.ordersService.findOne(id);
107
+ }
108
+ }
109
+
110
+ @Injectable()
111
+ export class OrdersService {
112
+ @ServiceLog()
113
+ async findOne(id: string) {
114
+ return this.calculateTotal(id);
115
+ }
116
+
117
+ @StepLog('calculate-total', {
118
+ payloadBuilder: (id: string) => ({ id }),
119
+ })
120
+ private calculateTotal(id: string) { /* ... */ }
121
+ }
122
+ ```
123
+
124
+ ---
125
+
126
+ ## Decoradores
127
+
128
+ ### `@RequestLog(options?)`
129
+
130
+ Aplica en métodos de **controller**. Emite `request.start`, `request.end` y `request.error`.
131
+
132
+ | Opción | Tipo | Descripción |
133
+ |---|---|---|
134
+ | `payloadBuilder` | `(...args) => object` | Extrae payload de los argumentos del método |
135
+
136
+ ### `@ServiceLog(options?)`
137
+
138
+ Aplica en métodos de **service**. Emite `service.enter`, `service.exit` y `service.error`.
139
+
140
+ | Opción | Tipo | Default |
141
+ |---|---|---|
142
+ | `payloadBuilder` | `(...args) => object` | — |
143
+ | `level` | `LogLevel` | `'info'` |
144
+ | `logExit` | `boolean` | `true` |
145
+
146
+ ### `@StepLog(stepName, options?)`
147
+
148
+ Aplica en métodos internos. Emite `step.<stepName>` y `step.<stepName>.error`.
149
+
150
+ | Opción | Tipo | Default |
151
+ |---|---|---|
152
+ | `payloadBuilder` | `(...args) => object` | — |
153
+ | `level` | `LogLevel` | `'debug'` |
154
+
155
+ ---
156
+
157
+ ## Logger y adaptadores
158
+
159
+ La librería usa **Pino** como backend por defecto.
160
+
161
+ ### Desarrollo — una línea por evento, colores
162
+
163
+ Requiere `pino-pretty`:
164
+
165
+ ```bash
166
+ npm install -D pino-pretty
167
+ ```
168
+
169
+ ```typescript
170
+ import { logger, PinoLogAdapter } from 'observability-kit';
171
+
172
+ logger.setAdapter(PinoLogAdapter.pretty());
173
+ ```
174
+
175
+ Output:
176
+
177
+ ```
178
+ [06:14:42] INFO: request.start | endpoint=/orders/1 | method=GET | req=46cfd3ae
179
+ [06:14:42] INFO: service.enter | service=OrdersService.findOne | req=46cfd3ae
180
+ [06:14:42] DEBUG: step.calculate-total | service=OrdersService.calculateTotal | req=46cfd3ae | payload={"id":"1"}
181
+ [06:14:42] INFO: service.exit | service=OrdersService.findOne | req=46cfd3ae
182
+ [06:14:42] INFO: request.end | endpoint=/orders/1 | method=GET | req=46cfd3ae | payload={"durationMs":12}
183
+ ```
184
+
185
+ Nivel mínimo:
186
+
187
+ ```typescript
188
+ logger.setAdapter(PinoLogAdapter.pretty({ level: 'info' })); // oculta debug
189
+ ```
190
+
191
+ ### Producción — JSON puro, async non-blocking
192
+
193
+ ```typescript
194
+ import pino from 'pino';
195
+ import { logger, PinoLogAdapter } from 'observability-kit';
196
+
197
+ const dest = pino.destination({ sync: false });
198
+ logger.setAdapter(new PinoLogAdapter({ destination: dest }));
199
+ ```
200
+
201
+ ### Adaptador personalizado
202
+
203
+ ```typescript
204
+ import type { ILogAdapter, LogLevel } from 'observability-kit';
205
+ import { logger } from 'observability-kit';
206
+
207
+ class MyAdapter implements ILogAdapter {
208
+ log(level: LogLevel, message: string, data: Record<string, unknown>): void {
209
+ // enviar a cualquier sistema
210
+ }
211
+ }
212
+
213
+ logger.setAdapter(new MyAdapter());
214
+ ```
215
+
216
+ ---
217
+
218
+ ## Plain Node.js
219
+
220
+ Sin NestJS. Importa desde `observability-kit`.
221
+
222
+ ```typescript
223
+ import { startContext, logger } from 'observability-kit';
224
+ import http from 'http';
225
+
226
+ http.createServer(async (req, res) => {
227
+ await startContext(
228
+ { requestId: req.headers['x-request-id'] as string, endpoint: req.url, method: req.method },
229
+ async () => {
230
+ logger.info('request.start', { service: 'http-server' });
231
+ res.end('OK');
232
+ logger.info('request.end', { service: 'http-server' });
233
+ },
234
+ );
235
+ }).listen(3000);
236
+ ```
237
+
238
+ ---
239
+
240
+ ## Context helpers
241
+
242
+ ```typescript
243
+ import { getContext, getRequestId, setContextField } from 'observability-kit';
244
+
245
+ const ctx = getContext(); // { requestId, endpoint?, method?, service? }
246
+ const id = getRequestId(); // string | undefined
247
+ setContextField('service', 'payment-worker');
248
+ ```
249
+
250
+ ---
251
+
252
+ ## Estructura del proyecto
253
+
254
+ ```
255
+ src/
256
+ types/options.ts
257
+ core/
258
+ context/request-context.ts
259
+ logger/
260
+ structured-logger.ts
261
+ adapters/
262
+ adapter.interface.ts
263
+ node.adapter.ts
264
+ pino.adapter.ts
265
+ nest/
266
+ adapters/nest.adapter.ts
267
+ decorators/
268
+ request-log.decorator.ts
269
+ service-log.decorator.ts
270
+ step-log.decorator.ts
271
+ interceptors/request.interceptor.ts
272
+ module/trace-logger.module.ts
273
+ index.ts
274
+ nestjs.ts
275
+ ```
276
+
277
+ ---
278
+
279
+ ## License
280
+
281
+ MIT
282
+
283
+ ![downloads](https://img.shields.io/npm/dm/observability-kit)
284
+ ![license](https://img.shields.io/npm/l/observability-kit)
285
+
286
+ ## Status
287
+
288
+ Early version – feedback welcome.
289
+
290
+ ---
291
+
292
+ ```typescript
293
+ import { RequestLog, ServiceLog, StepLog } from 'observability-kit/nestjs';
294
+
295
+ @Controller('users')
296
+ export class UsersController {
297
+ @Get()
298
+ @RequestLog()
299
+ findAll() { … }
300
+ }
301
+
302
+ @Injectable()
303
+ export class UsersService {
304
+ @ServiceLog()
305
+ async findAll() { … }
306
+
307
+ @StepLog('fetch-from-db')
308
+ private async fetchFromDb() { … }
309
+ }
310
+ ```
311
+
312
+ Every log line above is automatically enriched with the same `requestId`—no manual passing required.
313
+
314
+ ---
315
+
316
+ ## Why observability-kit?
317
+
318
+ Most logging libraries solve **logging**.
319
+
320
+ This library solves **request correlation** across controllers, services, and internal steps without passing IDs manually through the call chain.
321
+
322
+ ```
323
+ HTTP Request
324
+
325
+ TraceInterceptor ← reads / generates requestId
326
+
327
+ AsyncLocalStorage context ← requestId lives here for the full async chain
328
+
329
+ Controller (@RequestLog) ← logs request.start / request.end
330
+
331
+ Service (@ServiceLog) ← logs service.enter / service.exit
332
+
333
+ Step (@StepLog) ← logs step.<name>
334
+
335
+ Structured JSON logs ← every line carries requestId, service, timestamp
336
+ ```
337
+
338
+ With a single interceptor registration every downstream call—no matter how deep—carries the same `requestId` automatically.
339
+
340
+ ---
341
+
342
+ ## Quick Start
343
+
344
+ ```bash
345
+ npm install observability-kit
346
+ # NestJS peer deps:
347
+ npm install @nestjs/common @nestjs/core rxjs
348
+ ```
349
+
350
+ ```typescript
351
+ // app.module.ts
352
+ import { TraceLoggerModule } from 'observability-kit/nestjs';
353
+
354
+ @Module({ imports: [TraceLoggerModule.forRoot({ serviceName: 'my-api' })] })
355
+ export class AppModule {}
356
+
357
+ // main.ts
358
+ const app = await NestFactory.create(AppModule);
359
+ app.useGlobalInterceptors(app.get(TraceInterceptor)); // ← one line
360
+ await app.listen(3000);
361
+ ```
362
+
363
+ That's it. Every request now carries a correlated `requestId` through the full async chain.
364
+
365
+ ---
366
+
367
+ ## Features
368
+
369
+ - **Request-ID lifecycle** – reads `x-request-id` header or auto-generates a UUID v4; stored in `AsyncLocalStorage` for the full async chain.
370
+ - **Structured logs** – every entry is a plain JSON object `{ requestId, service, message, … }`. Payload is omitted when empty.
371
+ - **Pluggable adapters** – default writes to `stdout/stderr`; swap for pino, winston, or the built-in NestJS `Logger` with one line.
372
+ - **NestJS integration** – module, interceptor, and three method decorators.
373
+ - **Plain Node.js** – zero NestJS dependency; import from `observability-kit`.
374
+
375
+ ---
376
+
377
+ ## Install
378
+
379
+ ```bash
380
+ npm install observability-kit
381
+ ```
382
+
383
+ Peer dependencies (only needed for NestJS integration):
384
+
385
+ ```bash
386
+ npm install @nestjs/common @nestjs/core rxjs
387
+ ```
388
+
389
+ ---
390
+
391
+ ## Package entry points
392
+
393
+ | Import path | Contents |
394
+ |-----------------------------|----------------------------------|
395
+ | `observability-kit` | Core – context helpers + logger |
396
+ | `observability-kit/nestjs` | Core + NestJS module/decorators |
397
+
398
+ ---
399
+
400
+ ## NestJS Setup
401
+
402
+ ### 1 – Register the module
403
+
404
+ ```typescript
405
+ import { Module } from '@nestjs/common';
406
+ import { TraceLoggerModule } from 'observability-kit/nestjs';
407
+
408
+ @Module({
409
+ imports: [
410
+ TraceLoggerModule.forRoot({
411
+ serviceName: 'my-api',
412
+ requestIdHeaderName: 'x-request-id', // default
413
+ enableResponseHeader: true, // echo id back to client
414
+ }),
415
+ ],
416
+ })
417
+ export class AppModule {}
418
+ ```
419
+
420
+ ### 2 – Register the interceptor globally
421
+
422
+ ```typescript
423
+ // main.ts
424
+ import { NestFactory } from '@nestjs/core';
425
+ import { TraceInterceptor } from 'observability-kit/nestjs';
426
+ import { AppModule } from './app.module';
427
+
428
+ async function bootstrap() {
429
+ const app = await NestFactory.create(AppModule);
430
+ app.useGlobalInterceptors(app.get(TraceInterceptor));
431
+ await app.listen(3000);
432
+ }
433
+ bootstrap();
434
+ ```
435
+
436
+ The interceptor automatically:
437
+ - Reads / generates the `requestId`.
438
+ - Stores `{ requestId, endpoint, method }` in `AsyncLocalStorage`.
439
+ - Optionally sets the `x-request-id` response header.
440
+
441
+ ---
442
+
443
+ ## NestJS Decorators
444
+
445
+ ### `@RequestLog(options?)`
446
+
447
+ Apply to **controller** methods. Emits `request.start`, `request.end` (with `durationMs`), and `request.error`.
448
+
449
+ ```typescript
450
+ import { Controller, Get, Param } from '@nestjs/common';
451
+ import { RequestLog } from 'observability-kit/nestjs';
452
+
453
+ @Controller('users')
454
+ export class UsersController {
455
+ @Get(':id')
456
+ @RequestLog({
457
+ payloadBuilder: (id: string) => ({ id }),
458
+ })
459
+ async findOne(@Param('id') id: string) {
460
+ return this.usersService.findOne(id);
461
+ }
462
+ }
463
+ ```
464
+
465
+ Emitted logs:
466
+
467
+ ```json
468
+ { "level": "info", "message": "request.start", "requestId": "…", "endpoint": "/users/42", "method": "GET", "service": "UsersController.findOne", "payload": { "id": "42" } }
469
+ { "level": "info", "message": "request.end", "requestId": "…", "service": "UsersController.findOne", "payload": { "durationMs": 12 } }
470
+ ```
471
+
472
+ ---
473
+
474
+ ### `@ServiceLog(options?)`
475
+
476
+ Apply to **service / use-case** methods. Emits `service.enter`, `service.exit`, and `service.error`.
477
+
478
+ ```typescript
479
+ import { Injectable } from '@nestjs/common';
480
+ import { ServiceLog } from 'observability-kit/nestjs';
481
+
482
+ @Injectable()
483
+ export class UsersService {
484
+ @ServiceLog({
485
+ payloadBuilder: (id: string) => ({ id }),
486
+ logExit: true, // default true
487
+ level: 'info', // default 'info'
488
+ })
489
+ async findOne(id: string) {
490
+ return this.repo.findById(id);
491
+ }
492
+ }
493
+ ```
494
+
495
+ ---
496
+
497
+ ### `@StepLog(stepName, options?)`
498
+
499
+ Apply to internal / domain step methods. Emits `step.<stepName>` (default level `debug`).
500
+
501
+ ```typescript
502
+ import { StepLog } from 'observability-kit/nestjs';
503
+
504
+ export class PaymentService {
505
+ @StepLog('validate-amount', {
506
+ payloadBuilder: (amount: number) => ({ amount }),
507
+ level: 'debug', // default
508
+ })
509
+ private async validateAmount(amount: number) {
510
+ if (amount <= 0) throw new Error('Invalid amount');
511
+ }
512
+ }
513
+ ```
514
+
515
+ ---
516
+
517
+ ## Plain Node.js Setup
518
+
519
+ No NestJS required. Import from `observability-kit`.
520
+
521
+ ```typescript
522
+ import { startContext, logger } from 'observability-kit';
523
+ import http from 'http';
524
+
525
+ http.createServer(async (req, res) => {
526
+ const requestId = (req.headers['x-request-id'] as string) ?? undefined;
527
+
528
+ await startContext(
529
+ {
530
+ requestId,
531
+ endpoint: req.url,
532
+ method: req.method,
533
+ service: 'http-server',
534
+ },
535
+ async () => {
536
+ logger.info('request.start', { service: 'http-server' });
537
+
538
+ try {
539
+ // handle request …
540
+ logger.debug('step.process', { service: 'http-server', payload: { url: req.url } });
541
+ res.end('OK');
542
+ logger.info('request.end', { service: 'http-server' });
543
+ } catch (err) {
544
+ logger.error('request.error', { service: 'http-server', payload: { error: (err as Error).message } });
545
+ res.statusCode = 500;
546
+ res.end('Error');
547
+ }
548
+ },
549
+ );
550
+ }).listen(3000);
551
+ ```
552
+
553
+ Emitted log format:
554
+
555
+ ```json
556
+ { "level": "info", "message": "request.start", "timestamp": "2026-03-01T00:00:00.000Z", "service": "http-server", "requestId": "f47ac10b-…", "endpoint": "/", "method": "GET" }
557
+ ```
558
+
559
+ ---
560
+
561
+ ## Custom log adapter (pino example)
562
+
563
+ ```typescript
564
+ import pino from 'pino';
565
+ import { logger } from 'observability-kit';
566
+ import type { ILogAdapter, LogLevel } from 'observability-kit';
567
+
568
+ const pinoLogger = pino();
569
+
570
+ class PinoAdapter implements ILogAdapter {
571
+ log(level: LogLevel, _message: string, data: Record<string, unknown>): void {
572
+ pinoLogger[level](data);
573
+ }
574
+ }
575
+
576
+ // Swap once at startup – affects all subsequent logger calls globally
577
+ logger.setAdapter(new PinoAdapter());
578
+ ```
579
+
580
+ ---
581
+
582
+ ## Context helpers
583
+
584
+ ```typescript
585
+ import { getContext, getRequestId, setContextField } from 'observability-kit';
586
+
587
+ // Anywhere inside an active context (ALS chain):
588
+ const ctx = getContext(); // { requestId, endpoint?, method?, service? }
589
+ const id = getRequestId(); // string | undefined
590
+ setContextField('service', 'payment-worker'); // mutate current context
591
+ ```
592
+
593
+ ---
594
+
595
+ ## Structured log entry shape
596
+
597
+ ```
598
+ {
599
+ level: "debug" | "info" | "warn" | "error"
600
+ message: string // e.g. "request.start"
601
+ timestamp: string // ISO 8601
602
+ service: string // "ClassName.methodName"
603
+ requestId?: string // present when inside an ALS context
604
+ endpoint?: string // present in HTTP contexts
605
+ method?: string // present in HTTP contexts
606
+ payload?: object // omitted when empty / undefined
607
+ }
608
+ ```
609
+
610
+ ---
611
+
612
+ ## Project structure
613
+
614
+ ```
615
+ src/
616
+ types/
617
+ options.ts Shared TypeScript types + interfaces
618
+ core/
619
+ context/
620
+ request-context.ts AsyncLocalStorage – startContext / getContext / …
621
+ logger/
622
+ structured-logger.ts StructuredLogger class + `logger` singleton
623
+ adapters/
624
+ adapter.interface.ts ILogAdapter interface
625
+ node.adapter.ts Default adapter (stdout/stderr JSON lines)
626
+ nest/
627
+ adapters/
628
+ nest.adapter.ts NestJS Logger adapter
629
+ decorators/
630
+ request-log.decorator.ts @RequestLog
631
+ service-log.decorator.ts @ServiceLog
632
+ step-log.decorator.ts @StepLog
633
+ interceptors/
634
+ request.interceptor.ts TraceInterceptor (ALS context population)
635
+ module/
636
+ trace-logger.module.ts TraceLoggerModule.forRoot()
637
+ index.ts Core entry point (no NestJS dependency)
638
+ nestjs.ts NestJS entry point (core + NestJS integration)
639
+ ```
640
+
641
+ ---
642
+
643
+ ## Build
644
+
645
+ ```bash
646
+ npm run build # via NestJS CLI (nest build)
647
+ npm run build:tsc # via tsc directly
648
+ ```
649
+
650
+ ---
651
+
652
+ ## Roadmap
653
+
654
+ - [ ] OpenTelemetry support
655
+ - [ ] Fastify plugin
656
+ - [ ] Express middleware
657
+ - [ ] Performance benchmarks
658
+
659
+ ## License
660
+
661
+ MIT
@@ -0,0 +1,9 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
+ import type { RequestContext, StartContextOptions } from '../../types/options.js';
3
+ export declare const requestContextStorage: AsyncLocalStorage<RequestContext>;
4
+ export declare function startContext(options: StartContextOptions, callback: () => Promise<unknown> | unknown): Promise<unknown>;
5
+ export declare function runWithContext<T>(context: RequestContext, fn: () => T): T;
6
+ export declare function getContext(): RequestContext | undefined;
7
+ export declare function getRequestId(): string | undefined;
8
+ export declare function setContextField<K extends keyof RequestContext>(key: K, value: RequestContext[K]): void;
9
+ //# sourceMappingURL=request-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-context.d.ts","sourceRoot":"","sources":["../../../src/core/context/request-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAErD,OAAO,KAAK,EACV,cAAc,EACd,mBAAmB,EACpB,MAAM,wBAAwB,CAAC;AAEhC,eAAO,MAAM,qBAAqB,mCAA0C,CAAC;AAE7E,wBAAgB,YAAY,CAC1B,OAAO,EAAE,mBAAmB,EAC5B,QAAQ,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,GACzC,OAAO,CAAC,OAAO,CAAC,CAkBlB;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,OAAO,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAEzE;AAED,wBAAgB,UAAU,IAAI,cAAc,GAAG,SAAS,CAEvD;AAED,wBAAgB,YAAY,IAAI,MAAM,GAAG,SAAS,CAEjD;AAED,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,cAAc,EAC5D,GAAG,EAAE,CAAC,EACN,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,GACvB,IAAI,CAKN"}
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.requestContextStorage = void 0;
4
+ exports.startContext = startContext;
5
+ exports.runWithContext = runWithContext;
6
+ exports.getContext = getContext;
7
+ exports.getRequestId = getRequestId;
8
+ exports.setContextField = setContextField;
9
+ const node_async_hooks_1 = require("node:async_hooks");
10
+ const node_crypto_1 = require("node:crypto");
11
+ exports.requestContextStorage = new node_async_hooks_1.AsyncLocalStorage();
12
+ function startContext(options, callback) {
13
+ const context = {
14
+ requestId: options.requestId ?? (0, node_crypto_1.randomUUID)(),
15
+ ...(options.endpoint !== undefined ? { endpoint: options.endpoint } : {}),
16
+ ...(options.method !== undefined ? { method: options.method } : {}),
17
+ ...(options.service !== undefined ? { service: options.service } : {}),
18
+ };
19
+ return new Promise((resolve, reject) => {
20
+ exports.requestContextStorage.run(context, () => {
21
+ try {
22
+ const result = callback();
23
+ Promise.resolve(result).then(resolve).catch(reject);
24
+ }
25
+ catch (err) {
26
+ reject(err);
27
+ }
28
+ });
29
+ });
30
+ }
31
+ function runWithContext(context, fn) {
32
+ return exports.requestContextStorage.run(context, fn);
33
+ }
34
+ function getContext() {
35
+ return exports.requestContextStorage.getStore();
36
+ }
37
+ function getRequestId() {
38
+ return exports.requestContextStorage.getStore()?.requestId;
39
+ }
40
+ function setContextField(key, value) {
41
+ const store = exports.requestContextStorage.getStore();
42
+ if (store) {
43
+ store[key] = value;
44
+ }
45
+ }
46
+ //# sourceMappingURL=request-context.js.map