@infineit/winston-logger 1.0.29 → 1.0.31

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 (37) hide show
  1. package/README.md +1229 -115
  2. package/context/infrastructure/nestjs/contextModule.js +2 -4
  3. package/context/infrastructure/nestjs/contextModule.js.map +1 -1
  4. package/index.d.ts +14 -5
  5. package/index.js +20 -5
  6. package/index.js.map +1 -1
  7. package/logger/domain/log.d.ts +3 -1
  8. package/logger/domain/loggerService.d.ts +7 -4
  9. package/logger/domain/loggerService.js +140 -31
  10. package/logger/domain/loggerService.js.map +1 -1
  11. package/logger/domain/loggerTransport.d.ts +5 -0
  12. package/logger/domain/loggerTransport.js +5 -0
  13. package/logger/domain/loggerTransport.js.map +1 -0
  14. package/logger/domain/normalizedLog.d.ts +23 -0
  15. package/logger/domain/normalizedLog.js +37 -0
  16. package/logger/domain/normalizedLog.js.map +1 -0
  17. package/logger/infrastructure/nestjs/loggerModule.d.ts +12 -9
  18. package/logger/infrastructure/nestjs/loggerModule.js +72 -137
  19. package/logger/infrastructure/nestjs/loggerModule.js.map +1 -1
  20. package/logger/infrastructure/nestjs/nestjsLoggerServiceAdapter.js +61 -11
  21. package/logger/infrastructure/nestjs/nestjsLoggerServiceAdapter.js.map +1 -1
  22. package/logger/infrastructure/winston/transports/fileTransport.d.ts +1 -1
  23. package/logger/infrastructure/winston/transports/fileTransport.js +5 -2
  24. package/logger/infrastructure/winston/transports/fileTransport.js.map +1 -1
  25. package/logger/infrastructure/winston/winstonLogger.js +78 -27
  26. package/logger/infrastructure/winston/winstonLogger.js.map +1 -1
  27. package/logger/infrastructure/winston/winstonTransportAdapter.d.ts +10 -0
  28. package/logger/infrastructure/winston/winstonTransportAdapter.js +128 -0
  29. package/logger/infrastructure/winston/winstonTransportAdapter.js.map +1 -0
  30. package/package.json +11 -16
  31. package/tsconfig.lib.tsbuildinfo +1 -1
  32. package/logger/infrastructure/winston/transports/prisma-transport.d.ts +0 -11
  33. package/logger/infrastructure/winston/transports/prisma-transport.js +0 -50
  34. package/logger/infrastructure/winston/transports/prisma-transport.js.map +0 -1
  35. package/logger/levelFilter.d.ts +0 -2
  36. package/logger/levelFilter.js +0 -47
  37. package/logger/levelFilter.js.map +0 -1
package/README.md CHANGED
@@ -1,169 +1,1283 @@
1
- <br />
2
- <div align="center">
3
- <h3 align="center">NestJS Logger</h3>
1
+ # @infineit/winston-logger
4
2
 
5
- <p align="center">
6
- A Nest.js production-ready logger implementation.
7
- </p>
8
- </div>
3
+ Enterprise-level logging library for NestJS applications with support for multiple contexts (HTTP, Kafka, Bull/BullMQ, CRON, CLI) and flexible transport architecture.
9
4
 
5
+ ## Features
10
6
 
11
- ## Introduction
7
+ - ✅ **Multi-Context Support**: Works in HTTP services, Kafka consumers, Bull/BullMQ workers, CRON jobs, and CLI tasks
8
+ - ✅ **Transport Architecture**: Pluggable transport system with Winston as one transport among many
9
+ - ✅ **Log Normalization**: Automatic normalization and error serialization before transport
10
+ - ✅ **CLS Integration**: Correlation ID support via AsyncLocalStorage (CLS)
11
+ - ✅ **Fire-and-Forget**: Non-blocking, failure-safe logging that never breaks business logic
12
+ - ✅ **Zero Dependencies**: No database, HTTP framework, or ORM dependencies
13
+ - ✅ **Type-Safe**: Full TypeScript support with comprehensive type definitions
12
14
 
13
- `@infineit/winston-logger` is a lightweight project for implements a production-ready logger for Nest.js applications using Winston, Morgan and Prisma. This package simplifies the setup of logger and provides configurable options for instrumenting your application.
15
+ ## Table of Contents
14
16
 
17
+ - [Installation](#installation)
18
+ - [Quick Start](#quick-start)
19
+ - [Architecture Overview](#architecture-overview)
20
+ - [Configuration](#configuration)
21
+ - [Usage in Different Contexts](#usage-in-different-contexts)
22
+ - [Transport Architecture](#transport-architecture)
23
+ - [Log Normalization](#log-normalization)
24
+ - [Correlation ID (CLS)](#correlation-id-cls)
25
+ - [API Reference](#api-reference)
26
+ - [Migration Guide (Legacy app.useLogger Users)](#migration-guide-legacy-appuselogger-users)
27
+ - [Best Practices](#best-practices)
28
+ - [Forbidden Actions](#forbidden-actions)
29
+ - [Examples](#examples)
15
30
 
16
- ## Features
31
+ ---
17
32
 
18
- - **Easy Integration**: Quickly integrate logger with winston for distributed log.
19
- - **Configurable**: Provides options to customize logger configuration.
20
- - **Lightweight**: Minimal dependencies and overhead.
33
+ ## Installation
21
34
 
22
- That implements a production-ready system advanced or basic Microservices or Monoliths projects applying concepts like:
35
+ ```bash
36
+ npm install @infineit/winston-logger
37
+ ```
23
38
 
24
- * Correlation IDs
25
- * Decoupled log transporters
26
- * Log levels
27
- * Logging Rules
28
- * Log formatters
39
+ ### Peer Dependencies
29
40
 
30
- ## Installation
41
+ This package requires the following peer dependencies (usually already installed in NestJS projects):
31
42
 
32
- * You can install this package using npm:
33
- ```bash
34
- npm install @infineit/winston-logger
35
- ```
43
+ - `@nestjs/common` 11.1.9
44
+ - `@nestjs/config` 4.2.0
45
+ - `@nestjs/core` 11.1.9
46
+ - `nestjs-cls` 5.0.1
36
47
 
37
- Or using Yarn:
38
- ```bash
39
- yarn add @infineit/winston-logger
40
- ```
48
+ ---
41
49
 
42
- ### Prerequisites
50
+ ## Quick Start
43
51
 
44
- - NestJS
45
- - Winston
46
- - morgon
47
- - prisma
52
+ ### 1. Import Modules
48
53
 
49
- ### Getting Started
54
+ ```typescript
55
+ import { Module } from '@nestjs/common';
56
+ import { ConfigModule } from '@nestjs/config';
57
+ import { ContextModule, LoggerModule } from '@infineit/winston-logger';
50
58
 
51
- Follow these steps to set up and run logger. If all steps are followed correctly, logger should start without any issues:
59
+ @Module({
60
+ imports: [
61
+ ConfigModule.forRoot({ isGlobal: true }),
62
+ ContextModule, // Required for CLS (correlation ID support)
63
+ LoggerModule.forRoot(), // Configure logger
64
+ ],
65
+ })
66
+ export class AppModule {}
67
+ ```
52
68
 
53
- 1. To use `@infineit/winston-logger` in your NestJS application, simply import it of your `main.ts` file:
69
+ ### 2. Use LoggerService
54
70
 
55
- ```bash
56
- import { NestjsLoggerServiceAdapter } from '@infineit/winston-logger';;
71
+ ```typescript
72
+ import { Injectable } from '@nestjs/common';
73
+ import { LoggerService } from '@infineit/winston-logger';
57
74
 
58
- const app = await NestFactory.create(AppModule, {bufferLogs: true });
75
+ @Injectable()
76
+ export class MyService {
77
+ constructor(private readonly logger: LoggerService) {}
59
78
 
60
- app.useLogger(app.get(NestjsLoggerServiceAdapter));
61
- ```
79
+ doSomething() {
80
+ this.logger.info('Processing started', {
81
+ userId: 123,
82
+ action: 'process',
83
+ });
62
84
 
63
- 2. import in app.module.ts:
85
+ try {
86
+ // Your business logic
87
+ } catch (error) {
88
+ this.logger.error(error, {
89
+ userId: 123,
90
+ action: 'process',
91
+ });
92
+ }
93
+ }
94
+ }
95
+ ```
64
96
 
65
- ```bash
66
- import { ContextModule, LoggerModule } from '@infineit/winston-logger';
97
+ ---
67
98
 
68
- @Module({
69
- imports: [
70
- ContextModule,
71
- LoggerModule.forRoot(PrismaService),
72
- ]
73
- })
74
- ```
99
+ ## Architecture Overview
75
100
 
76
- 3. app.service.ts:
101
+ ### LoggerService → LoggerTransport[] Flow
77
102
 
78
- ```bash
79
- import Logger, { LoggerKey } from '@infineit/winston-logger';
103
+ ```
104
+ ┌─────────────────────────────────────────────────────────────┐
105
+ │ Application Code │
106
+ │ (HTTP Services, Kafka Consumers, Bull Workers, etc.) │
107
+ └────────────────────┬────────────────────────────────────────┘
108
+
109
+
110
+ ┌─────────────────────────────────────────────────────────────┐
111
+ │ LoggerService │
112
+ │ • Normalizes logs │
113
+ │ • Serializes errors │
114
+ │ • Retrieves correlationId from CLS │
115
+ │ • Sends to all transports (fire-and-forget) │
116
+ └────────────────────┬────────────────────────────────────────┘
117
+
118
+
119
+ ┌───────────────────────┐
120
+ │ NormalizedLog │
121
+ │ • timestamp │
122
+ │ • level │
123
+ │ • message │
124
+ │ • error (serialized)│
125
+ │ • correlationId │
126
+ │ • ... │
127
+ └───────────────────────┘
128
+
129
+
130
+ ┌─────────────────────────────────────────────────────────────┐
131
+ │ LoggerTransport[] (Array) │
132
+ └────────────────────┬────────────────────────────────────────┘
133
+
134
+ ┌────────────┼────────────┐
135
+ │ │ │
136
+ ▼ ▼ ▼
137
+ ┌──────────────┐ ┌──────────┐ ┌──────────┐
138
+ │WinstonTransport│ │ Custom │ │ Custom │
139
+ │ Adapter │ │ Transport│ │ Transport│
140
+ │ │ │ │ │ │
141
+ │ • Console │ │ • Kafka │ │ • HTTP │
142
+ │ • File │ │ • Database│ │ • etc. │
143
+ │ • Slack │ │ │ │ │
144
+ └──────────────┘ └──────────┘ └──────────┘
145
+ ```
80
146
 
81
- constructor(@Inject(LoggerKey) private logger: Logger) {}
147
+ ### Key Components
82
148
 
83
- this.logger.info('I am an info message!', {
84
- props: {
85
- foo: 'bar',
86
- baz: 'qux',
87
- },
88
- });
89
- ```
149
+ 1. **LoggerService**: Main logging service that normalizes logs and sends them to transports
150
+ 2. **LoggerTransport**: Interface for all log transports (Winston, Kafka, HTTP, Database, etc.)
151
+ 3. **WinstonTransportAdapter**: Wraps Winston as one transport among many
152
+ 4. **ContextModule**: Provides CLS (AsyncLocalStorage) for correlation ID management
153
+ 5. **NormalizedLog**: Standardized log format sent to all transports
90
154
 
91
- **Reminder**: Make sure prisma is initialize and winstonlog modal is running before starting your NestJS application to avoid initialization errors.
155
+ ---
92
156
 
93
157
  ## Configuration
94
158
 
95
- You can configure the Logger using environment variables in your `.env` file:
159
+ ### LoggerModule Configuration
160
+
161
+ ```typescript
162
+ import { LoggerModule, LoggerModuleConfig } from '@infineit/winston-logger';
163
+
164
+ const config: LoggerModuleConfig = {
165
+ nodeEnv: 'production', // 'production' | 'testing' | 'development'
166
+ slack_webhook: 'https://...', // Slack webhook URL (optional)
167
+ console_print: true, // Enable console output (boolean or 'true'/'false')
168
+ log_in_file: true, // Enable file logging (boolean or 'true'/'false')
169
+ organization: 'my-org', // Organization name (optional)
170
+ context: 'user-service', // Bounded context name (optional)
171
+ app: 'api-gateway', // Application name (optional)
172
+ };
173
+
174
+ @Module({
175
+ imports: [
176
+ LoggerModule.forRoot(config),
177
+ ],
178
+ })
179
+ export class AppModule {}
180
+ ```
181
+
182
+ ### Configuration via Environment Variables
183
+
184
+ You can also configure via `@nestjs/config`:
185
+
186
+ ```typescript
187
+ // .env
188
+ NODE_ENV=production
189
+ SLACK_WEBHOOK=https://hooks.slack.com/services/...
190
+ CONSOLE_PRINT=true
191
+ LOG_IN_FILE=true
192
+ LOGGER_ORGANIZATION=my-org
193
+ LOGGER_CONTEXT=user-service
194
+ LOGGER_APP=api-gateway
195
+ ```
196
+
197
+ ```typescript
198
+ // logger.config.ts
199
+ import { registerAs } from '@nestjs/config';
200
+
201
+ export default registerAs('logger', () => ({
202
+ nodeEnv: process.env.NODE_ENV,
203
+ slack_webhook: process.env.SLACK_WEBHOOK,
204
+ console_print: process.env.CONSOLE_PRINT,
205
+ log_in_file: process.env.LOG_IN_FILE,
206
+ organization: process.env.LOGGER_ORGANIZATION,
207
+ context: process.env.LOGGER_CONTEXT,
208
+ app: process.env.LOGGER_APP,
209
+ }));
210
+ ```
211
+
212
+ ```typescript
213
+ @Module({
214
+ imports: [
215
+ ConfigModule.forRoot({
216
+ isGlobal: true,
217
+ load: [loggerConfig],
218
+ }),
219
+ LoggerModule.forRoot(), // Will read from ConfigService
220
+ ],
221
+ })
222
+ export class AppModule {}
223
+ ```
224
+
225
+ ### Configuration Options
226
+
227
+ | Option | Type | Required | Default | Description |
228
+ |--------|------|----------|---------|-------------|
229
+ | `nodeEnv` | `string` | No | `undefined` | Environment: `'production'`, `'testing'`, or `'development'` |
230
+ | `slack_webhook` | `string` | No | `undefined` | Slack webhook URL for fatal error notifications |
231
+ | `console_print` | `boolean \| string` | No | `false` | Enable console output (`true`/`false` or `'true'`/`'false'`) |
232
+ | `log_in_file` | `boolean \| string` | No | `false` | Enable file logging (`true`/`false` or `'true'`/`'false'`) |
233
+ | `organization` | `string` | No | `undefined` | Organization or project name |
234
+ | `context` | `string` | No | `undefined` | Bounded context name |
235
+ | `app` | `string` | No | `undefined` | Application or microservice name |
236
+
237
+ ### Default Behavior
238
+
239
+ - **Development/Testing**: Console and file logging enabled by default
240
+ - **Production**: Console and file logging disabled by default (unless explicitly enabled)
241
+ - **Slack**: Only enabled in production/testing when `slack_webhook` is provided
242
+
243
+ ---
244
+
245
+ ## Usage in Different Contexts
246
+
247
+ ### HTTP Services
248
+
249
+ ```typescript
250
+ import { Injectable } from '@nestjs/common';
251
+ import { LoggerService } from '@infineit/winston-logger';
252
+
253
+ @Injectable()
254
+ export class UserController {
255
+ constructor(private readonly logger: LoggerService) {}
256
+
257
+ @Get('/users/:id')
258
+ async getUser(@Param('id') id: string) {
259
+ // Correlation ID is automatically retrieved from CLS
260
+ // (if CLS middleware is configured in your app)
261
+ this.logger.info('Fetching user', { userId: id });
262
+
263
+ try {
264
+ const user = await this.userService.findById(id);
265
+ return user;
266
+ } catch (error) {
267
+ this.logger.error(error, { userId: id });
268
+ throw error;
269
+ }
270
+ }
271
+ }
272
+ ```
273
+
274
+ **Important**: For HTTP services, you must configure CLS middleware manually:
275
+
276
+ ```typescript
277
+ import { ClsModule } from 'nestjs-cls';
278
+
279
+ @Module({
280
+ imports: [
281
+ ClsModule.forRoot({
282
+ middleware: {
283
+ mount: true, // Enable middleware
284
+ generateId: true, // Auto-generate correlation ID
285
+ idGenerator: (req: Request) => req.headers['x-correlation-id'] || uuidv4(),
286
+ },
287
+ }),
288
+ ContextModule, // Provides CLS service
289
+ LoggerModule.forRoot(),
290
+ ],
291
+ })
292
+ export class AppModule {}
293
+ ```
294
+
295
+ ### Kafka Consumers
296
+
297
+ ```typescript
298
+ import { Injectable } from '@nestjs/common';
299
+ import { LoggerService } from '@infineit/winston-logger';
300
+ import { ContextStorageService } from '@infineit/winston-logger';
301
+
302
+ @Injectable()
303
+ export class OrderConsumer {
304
+ constructor(
305
+ private readonly logger: LoggerService,
306
+ private readonly contextStorage: ContextStorageService,
307
+ ) {}
308
+
309
+ @KafkaListener('order.created')
310
+ async handleOrderCreated(message: OrderCreatedEvent) {
311
+ // Set correlation ID from Kafka message
312
+ this.contextStorage.setContextId(message.correlationId || message.id);
313
+
314
+ this.logger.info('Processing order', {
315
+ orderId: message.id,
316
+ userId: message.userId,
317
+ });
318
+
319
+ try {
320
+ await this.processOrder(message);
321
+ } catch (error) {
322
+ this.logger.error(error, {
323
+ orderId: message.id,
324
+ });
325
+ }
326
+ }
327
+ }
328
+ ```
329
+
330
+ ### Bull/BullMQ Workers
331
+
332
+ ```typescript
333
+ import { Injectable } from '@nestjs/common';
334
+ import { Processor, Process } from '@nestjs/bull';
335
+ import { Job } from 'bull';
336
+ import { LoggerService } from '@infineit/winston-logger';
337
+ import { ContextStorageService } from '@infineit/winston-logger';
338
+
339
+ @Processor('email')
340
+ export class EmailProcessor {
341
+ constructor(
342
+ private readonly logger: LoggerService,
343
+ private readonly contextStorage: ContextStorageService,
344
+ ) {}
345
+
346
+ @Process('send')
347
+ async handleSendEmail(job: Job<EmailJob>) {
348
+ // Set correlation ID from job
349
+ this.contextStorage.setContextId(job.id.toString());
350
+
351
+ this.logger.info('Sending email', {
352
+ jobId: job.id,
353
+ email: job.data.to,
354
+ });
355
+
356
+ try {
357
+ await this.emailService.send(job.data);
358
+ this.logger.info('Email sent', {
359
+ jobId: job.id,
360
+ durationMs: Date.now() - job.timestamp,
361
+ });
362
+ } catch (error) {
363
+ this.logger.error(error, {
364
+ jobId: job.id,
365
+ });
366
+ throw error;
367
+ }
368
+ }
369
+ }
370
+ ```
371
+
372
+ ### CRON Jobs
373
+
374
+ ```typescript
375
+ import { Injectable } from '@nestjs/common';
376
+ import { Cron, CronExpression } from '@nestjs/schedule';
377
+ import { LoggerService } from '@infineit/winston-logger';
378
+ import { ContextStorageService } from '@infineit/winston-logger';
379
+
380
+ @Injectable()
381
+ export class CleanupService {
382
+ constructor(
383
+ private readonly logger: LoggerService,
384
+ private readonly contextStorage: ContextStorageService,
385
+ ) {}
386
+
387
+ @Cron(CronExpression.EVERY_HOUR)
388
+ async cleanup() {
389
+ // Set correlation ID for this job
390
+ this.contextStorage.setContextId(`cleanup-${Date.now()}`);
391
+
392
+ this.logger.info('Starting cleanup job');
393
+
394
+ try {
395
+ const deleted = await this.repository.deleteOldRecords();
396
+ this.logger.info('Cleanup completed', {
397
+ deletedCount: deleted,
398
+ });
399
+ } catch (error) {
400
+ this.logger.error(error);
401
+ }
402
+ }
403
+ }
404
+ ```
405
+
406
+ ### CLI Tasks
407
+
408
+ ```typescript
409
+ import { Injectable } from '@nestjs/common';
410
+ import { LoggerService } from '@infineit/winston-logger';
411
+ import { ContextStorageService } from '@infineit/winston-logger';
412
+
413
+ @Injectable()
414
+ export class MigrationService {
415
+ constructor(
416
+ private readonly logger: LoggerService,
417
+ private readonly contextStorage: ContextStorageService,
418
+ ) {}
419
+
420
+ async runMigration() {
421
+ // Set correlation ID for this task
422
+ this.contextStorage.setContextId(`migration-${Date.now()}`);
423
+
424
+ this.logger.info('Starting migration');
425
+
426
+ try {
427
+ await this.migrate();
428
+ this.logger.info('Migration completed');
429
+ } catch (error) {
430
+ this.logger.error(error);
431
+ process.exit(1);
432
+ }
433
+ }
434
+ }
435
+ ```
436
+
437
+ ---
438
+
439
+ ## Transport Architecture
440
+
441
+ ### Built-in Transports
442
+
443
+ The library includes `WinstonTransportAdapter` which wraps Winston and provides:
444
+
445
+ - **ConsoleTransport**: Colorized console output
446
+ - **FileTransport**: Daily rotating file logs (`logs/log-YYYY-MM-DD-HH.log`)
447
+ - **SlackTransport**: Fatal error notifications to Slack
448
+
449
+ ### Custom Transports
450
+
451
+ You can create custom transports by implementing the `LoggerTransport` interface:
452
+
453
+ ```typescript
454
+ import { Injectable } from '@nestjs/common';
455
+ import { LoggerTransport, NormalizedLog } from '@infineit/winston-logger';
456
+
457
+ @Injectable()
458
+ export class KafkaTransport implements LoggerTransport {
459
+ constructor(private readonly kafkaProducer: KafkaProducer) {}
460
+
461
+ log(normalizedLog: NormalizedLog): void {
462
+ try {
463
+ // Send to Kafka (fire-and-forget)
464
+ this.kafkaProducer.send({
465
+ topic: 'logs',
466
+ messages: [{
467
+ value: JSON.stringify(normalizedLog),
468
+ }],
469
+ });
470
+ } catch (error) {
471
+ // Swallow errors - never break business logic
472
+ console.error('KafkaTransport error (swallowed):', error);
473
+ }
474
+ }
475
+ }
476
+ ```
477
+
478
+ ### Registering Custom Transports
479
+
480
+ ```typescript
481
+ import { Provider } from '@nestjs/common';
482
+ import { LoggerModule } from '@infineit/winston-logger';
483
+ import { KafkaTransport } from './kafka-transport';
484
+
485
+ const customTransports: Provider[] = [
486
+ {
487
+ provide: KafkaTransport,
488
+ useClass: KafkaTransport,
489
+ },
490
+ ];
491
+
492
+ @Module({
493
+ imports: [
494
+ LoggerModule.forRoot(config, customTransports),
495
+ ],
496
+ })
497
+ export class AppModule {}
498
+ ```
499
+
500
+ ### Transport Requirements
501
+
502
+ All transports must:
503
+
504
+ 1. ✅ Implement `LoggerTransport` interface
505
+ 2. ✅ Accept `NormalizedLog` (never raw `Error` objects)
506
+ 3. ✅ Be fire-and-forget (non-blocking)
507
+ 4. ✅ Never throw errors
508
+ 5. ✅ Handle failures gracefully
509
+
510
+ ---
511
+
512
+ ## Log Normalization
513
+
514
+ ### Automatic Normalization
515
+
516
+ All logs are automatically normalized before being sent to transports:
517
+
518
+ ```typescript
519
+ // You can pass Error objects or LogData with errors
520
+ this.logger.error(new Error('Something went wrong'), {
521
+ userId: 123,
522
+ });
523
+
524
+ // LoggerService automatically:
525
+ // 1. Serializes the Error to SerializedError
526
+ // 2. Creates NormalizedLog with all required fields
527
+ // 3. Sends to all transports
528
+ ```
529
+
530
+ ### NormalizedLog Structure
531
+
532
+ ```typescript
533
+ interface NormalizedLog {
534
+ timestamp: number; // Unix timestamp in milliseconds
535
+ level: LogLevel; // 'debug' | 'info' | 'warn' | 'error' | 'fatal' | 'emergency'
536
+ message: string; // Log message
537
+ organization?: string; // From config
538
+ context?: string; // From config
539
+ app?: string; // From config
540
+ sourceClass?: string; // Auto-detected from class name
541
+ correlationId?: string; // From CLS (can be undefined)
542
+ error?: SerializedError; // Serialized error (never raw Error)
543
+ props?: Record<string, any>; // Custom properties
544
+ durationMs?: number; // Duration in milliseconds
545
+ label?: string; // Auto-generated: `${organization}.${context}.${app}`
546
+ stack?: string; // Error stack trace (if error present)
547
+ }
548
+ ```
549
+
550
+ ### Error Serialization
551
+
552
+ Errors are automatically serialized using `serializeError()`:
553
+
554
+ ```typescript
555
+ import { serializeError } from '@infineit/winston-logger';
556
+
557
+ const error = new Error('Test error');
558
+ error.code = 'ERR_TEST';
559
+
560
+ const serialized = serializeError(error);
561
+ // {
562
+ // name: 'Error',
563
+ // message: 'Test error',
564
+ // stack: 'Error: Test error\n at ...',
565
+ // code: 'ERR_TEST'
566
+ // }
567
+ ```
568
+
569
+ **Important**: If you pass errors in `LogData`, they must already be serialized:
570
+
571
+ ```typescript
572
+ import { serializeError } from '@infineit/winston-logger';
573
+
574
+ const error = new Error('Test');
575
+ this.logger.info('Message', {
576
+ error: serializeError(error), // Must serialize manually if in LogData
577
+ });
578
+ ```
579
+
580
+ ---
581
+
582
+ ## Correlation ID (CLS)
583
+
584
+ ### Automatic Retrieval
585
+
586
+ `LoggerService` automatically retrieves correlation ID from CLS:
587
+
588
+ ```typescript
589
+ // If CLS is configured, correlationId is automatically included
590
+ this.logger.info('Message');
591
+ // Log includes: { correlationId: '...' } (from CLS)
592
+ ```
593
+
594
+ ### Manual Setting (Non-HTTP Contexts)
595
+
596
+ For non-HTTP contexts (Kafka, Bull, CRON, CLI), set correlation ID manually:
597
+
598
+ ```typescript
599
+ import { ContextStorageService } from '@infineit/winston-logger';
600
+
601
+ constructor(
602
+ private readonly logger: LoggerService,
603
+ private readonly contextStorage: ContextStorageService,
604
+ ) {}
605
+
606
+ async processMessage(message: Message) {
607
+ // Set correlation ID from message
608
+ this.contextStorage.setContextId(message.correlationId);
609
+
610
+ this.logger.info('Processing message');
611
+ // Log includes: { correlationId: message.correlationId }
612
+ }
613
+ ```
614
+
615
+ ### Optional Correlation ID
616
+
617
+ Correlation ID is **optional** - `undefined` is valid:
618
+
619
+ ```typescript
620
+ // If no correlation ID is set, logs will have:
621
+ // { correlationId: undefined }
622
+ // This is perfectly valid and expected in some contexts
623
+ ```
624
+
625
+ ### HTTP Context Setup
626
+
627
+ For HTTP services, configure CLS middleware:
628
+
629
+ ```typescript
630
+ import { ClsModule } from 'nestjs-cls';
631
+ import { v4 as uuidv4 } from 'uuid';
632
+
633
+ @Module({
634
+ imports: [
635
+ ClsModule.forRoot({
636
+ middleware: {
637
+ mount: true,
638
+ generateId: true,
639
+ idGenerator: (req: Request) => {
640
+ // Use existing correlation ID from header, or generate new one
641
+ return req.headers['x-correlation-id'] as string || uuidv4();
642
+ },
643
+ },
644
+ }),
645
+ ContextModule, // Provides CLS service
646
+ LoggerModule.forRoot(),
647
+ ],
648
+ })
649
+ export class AppModule {}
650
+ ```
651
+
652
+ ---
653
+
654
+ ## API Reference
655
+
656
+ ### LoggerService
657
+
658
+ Main logging service injected into your classes.
659
+
660
+ #### Methods
661
+
662
+ ```typescript
663
+ // Generic log method
664
+ log(level: LogLevel, message: string | Error, data?: LogData, profile?: string): void
665
+
666
+ // Convenience methods
667
+ debug(message: string, data?: LogData, profile?: string): void
668
+ info(message: string, data?: LogData, profile?: string): void
669
+ warn(message: string | Error, data?: LogData, profile?: string): void
670
+ error(message: string | Error, data?: LogData, profile?: string): void
671
+ fatal(message: string | Error, data?: LogData, profile?: string): void
672
+ emergency(message: string | Error, data?: LogData, profile?: string): void
673
+
674
+ // Profile (currently not implemented in transport architecture)
675
+ startProfile(id: string): void
676
+ ```
677
+
678
+ #### LogLevel
96
679
 
97
- - `NODE_ENV`: development / staging / testing / production.
98
- - `LOGGER_ORGANIZATION`: The name of your organization as it will appear in log.
99
- - `LOGGER_CONTEXT`: The name of your context as it will appear in log.
100
- - `LOGGER_APP`: The name of your app as it will appear in log.
101
- - `LOGGER_DATABASE_STORAGE`: True/False. Default True for production or testing, It will store log to database.
102
- - `LOGGER_LOG_LEVEL`: Default log level warn,error,fatal for production or testing, It will log.
103
- - `LOGGER_DURATION`: True/False. Default False, It will store request duration in database.
104
- - `LOGGER_DURATION_LOG_LEVEL`: Default log level warn,error,fatal for production or testing, It will calculate duration for request.
105
- - `LOGGER_CONSOLE_PRINT`: True/False. Default False for production or testing.
106
- - `LOGGER_LOG_IN_FILE`: True/False. Default False for production or testing.
107
- - `LOGGER_SLACK_INC_WEBHOOK_URL`: Slack url, eg. `https://hooks.slack.com/services/XXXXXXXXX/XXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXX`.
680
+ ```typescript
681
+ enum LogLevel {
682
+ Emergency = 'emergency', // One or more systems are unusable
683
+ Fatal = 'fatal', // A person must take an action immediately
684
+ Error = 'error', // Error events are likely to cause problems
685
+ Warn = 'warn', // Warning events might cause problems
686
+ Info = 'info', // Routine information
687
+ Debug = 'debug', // Debug or trace information
688
+ }
689
+ ```
108
690
 
109
- ## Project structure
691
+ #### LogData
110
692
 
111
- ## Key concepts
693
+ ```typescript
694
+ interface LogData {
695
+ organization?: string; // Override config organization
696
+ context?: string; // Override config context
697
+ app?: string; // Override config app
698
+ sourceClass?: string; // Override auto-detected class name
699
+ correlationId?: string; // Override CLS correlation ID
700
+ error?: SerializedError; // Error (must be serialized)
701
+ props?: Record<string, any>; // Custom properties
702
+ durationMs?: number; // Duration in milliseconds
703
+ }
704
+ ```
112
705
 
113
- ### NestJS Logger
706
+ ### ContextStorageService
114
707
 
115
- NestJS uses a custom logger for bootstrap and internal logging. To use our logger, we need to create an adapter that implements the NestJS `LoggerService` interface. That is implemented in the `NestjsLoggerServiceAdapter` class.
708
+ Service for managing CLS context (correlation ID).
116
709
 
117
- We pass that adapter to the NestJS app on the `main.ts` file.
710
+ #### Methods
118
711
 
119
- ### Correlation IDs
712
+ ```typescript
713
+ // Get correlation ID from CLS
714
+ getContextId(): string | undefined
120
715
 
121
- To manage correlation IDs, we use `nestjs-cls` library that implements a Local Storage. With that, we can isolate and share data on a request lifecycle.
716
+ // Set correlation ID in CLS
717
+ setContextId(id: string): void
122
718
 
123
- The system reads the `x-correlation-id` HTTP header and stores it on the Local Storage. If the header is not present, the system generates a new UUID and stores it on the Local Storage.
719
+ // Get any value from CLS
720
+ get<T>(key: string): T | undefined
124
721
 
125
- ### Context Wrapper
722
+ // Set any value in CLS
723
+ set<T>(key: string, value: T): void
724
+ ```
126
725
 
127
- To add custom data to all the logs, we use a wrapper `LoggerContextWrapper`.
726
+ ---
128
727
 
129
- That class is injected with a Transient scope. By that, we can get the caller class and add it to the logs.
728
+ ## Migration Guide (Legacy app.useLogger Users)
130
729
 
131
- ## Prisma Table
730
+ ### ⚠️ Deprecated: app.useLogger() and app.flushLogs()
132
731
 
133
- ```
134
- model winstonlog {
135
- id_log String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
136
- level String @db.VarChar(80)
137
- message String @db.Text
138
- context String? @db.VarChar(255)
139
- correlationId String? @db.Uuid
140
- sourceClass String? @db.VarChar(255)
141
- props Json?
142
- organization String? @db.VarChar(40)
143
- app String? @db.VarChar(40)
144
- durationMs Decimal? @default(0) @db.Decimal(10, 4)
145
- stack String? @db.Text
146
- label String? @db.VarChar(40)
147
- timestamp DateTime @default(now()) @db.Timestamptz(6)
732
+ If you're migrating from an older version of `@infineit/winston-logger` that used `app.useLogger()` and `app.flushLogs()`, this section is for you.
148
733
 
149
- @@schema("public")
150
- }
151
- ```
152
- ## Inspiration
734
+ **Status**: These methods have been **removed** and are **no longer available**.
153
735
 
154
- This project is inspired by the excellent work from:
736
+ ### Why Was It Removed?
155
737
 
156
- - [Medium](https://medium.com/@jose-luis-navarro/logging-on-nestjs-like-a-pro-with-correlation-ids-log-aggregation-winston-morgan-and-more-d03e3bb56772)
157
- - [Logger](https://github.com/jnm733/nestjs-logger/tree/main)
158
- - [winston-pg](https://github.com/InnovA2/winston-pg/tree/main)
738
+ The `app.useLogger()` and `app.flushLogs()` methods were removed for the following architectural reasons:
739
+
740
+ 1. **HTTP Assumption Violation**: These methods assumed HTTP runtime, making the logger unusable in Kafka consumers, Bull workers, CRON jobs, and CLI tasks.
741
+
742
+ 2. **Middleware Auto-Mounting**: The old pattern auto-mounted middleware, violating the library's core principle that applications must configure middleware themselves.
743
+
744
+ 3. **Transport Architecture**: The new architecture uses a pluggable `LoggerTransport[]` system where Winston is one transport among many, not the only transport.
745
+
746
+ 4. **Fire-and-Forget Guarantee**: The new architecture ensures all logging is truly fire-and-forget without requiring explicit `flushLogs()` calls.
747
+
748
+ ### Old Deprecated Usage ❌
749
+
750
+ ```typescript
751
+ // ❌ DEPRECATED - This no longer works
752
+ import { NestFactory } from '@nestjs/core';
753
+ import { LoggerModule } from '@infineit/winston-logger';
754
+
755
+ async function bootstrap() {
756
+ const app = await NestFactory.create(AppModule);
757
+
758
+ // ❌ DEPRECATED: app.useLogger() is removed
759
+ app.useLogger(app.get(LoggerModule));
760
+
761
+ await app.listen(3000);
762
+
763
+ // ❌ DEPRECATED: app.flushLogs() is removed
764
+ app.flushLogs();
765
+ }
766
+
767
+ bootstrap();
768
+ ```
769
+
770
+ ### New Correct Usage ✅
771
+
772
+ ```typescript
773
+ // ✅ CORRECT: Use LoggerService injection
774
+ import { NestFactory } from '@nestjs/core';
775
+ import { ContextModule, LoggerModule } from '@infineit/winston-logger';
776
+
777
+ @Module({
778
+ imports: [
779
+ ContextModule,
780
+ LoggerModule.forRoot(),
781
+ ],
782
+ })
783
+ export class AppModule {}
784
+
785
+ async function bootstrap() {
786
+ const app = await NestFactory.create(AppModule);
787
+
788
+ // ✅ No app.useLogger() needed - LoggerService is automatically available
789
+ // ✅ No app.flushLogs() needed - logging is fire-and-forget
790
+
791
+ await app.listen(3000);
792
+ }
793
+
794
+ bootstrap();
795
+ ```
796
+
797
+ ### Using LoggerService in Your Code
798
+
799
+ ```typescript
800
+ // ✅ CORRECT: Inject LoggerService
801
+ import { Injectable } from '@nestjs/common';
802
+ import { LoggerService } from '@infineit/winston-logger';
803
+
804
+ @Injectable()
805
+ export class MyService {
806
+ constructor(private readonly logger: LoggerService) {}
807
+
808
+ doSomething() {
809
+ // ✅ Logging is automatic and fire-and-forget
810
+ this.logger.info('Processing started');
811
+
812
+ // ✅ No need to flush - logs are sent immediately
813
+ // ✅ No need to await - logging is non-blocking
814
+ }
815
+ }
816
+ ```
817
+
818
+ ### Comparison Table
819
+
820
+ | Feature | Old (Deprecated) ❌ | New (Current) ✅ |
821
+ |---------|---------------------|------------------|
822
+ | **Setup** | `app.useLogger(...)` | `LoggerModule.forRoot()` in imports |
823
+ | **Flush** | `app.flushLogs()` required | Not needed (fire-and-forget) |
824
+ | **Usage** | Direct logger access | Inject `LoggerService` |
825
+ | **HTTP Only** | Yes (assumed HTTP runtime) | No (works in all contexts) |
826
+ | **Middleware** | Auto-mounted | Manual configuration |
827
+ | **Transports** | Winston only | Pluggable `LoggerTransport[]` |
828
+ | **Blocking** | Could block with `flushLogs()` | Always non-blocking |
829
+ | **Context Support** | HTTP only | HTTP, Kafka, Bull, CRON, CLI |
830
+
831
+ ### Key Changes Summary
832
+
833
+ #### ❌ What's Removed
834
+
835
+ - `app.useLogger()` - **No longer exists**
836
+ - `app.flushLogs()` - **No longer exists**
837
+ - HTTP-only assumptions - **Removed**
838
+ - Auto-mounted middleware - **Removed**
839
+
840
+ #### ✅ What's New
841
+
842
+ - **LoggerService injection** - Inject `LoggerService` into your classes
843
+ - **Multi-context support** - Works in HTTP, Kafka, Bull, CRON, CLI
844
+ - **Transport architecture** - Pluggable transports via `LoggerTransport[]`
845
+ - **Automatic normalization** - Logs are normalized before transport
846
+ - **CLS integration** - Correlation ID support via AsyncLocalStorage
847
+
848
+ ### Migration Steps
849
+
850
+ 1. **Remove `app.useLogger()` calls**
851
+ ```typescript
852
+ // ❌ Remove this
853
+ app.useLogger(app.get(LoggerModule));
854
+ ```
855
+
856
+ 2. **Remove `app.flushLogs()` calls**
857
+ ```typescript
858
+ // ❌ Remove this
859
+ app.flushLogs();
860
+ ```
861
+
862
+ 3. **Add LoggerModule to imports**
863
+ ```typescript
864
+ @Module({
865
+ imports: [
866
+ ContextModule, // Required for CLS
867
+ LoggerModule.forRoot(), // Add this
868
+ ],
869
+ })
870
+ export class AppModule {}
871
+ ```
872
+
873
+ 4. **Inject LoggerService instead of direct logger access**
874
+ ```typescript
875
+ // ❌ Old way (if you had direct logger access)
876
+ // logger.info('message');
877
+
878
+ // ✅ New way
879
+ constructor(private readonly logger: LoggerService) {}
880
+ this.logger.info('message');
881
+ ```
882
+
883
+ 5. **Configure CLS middleware (for HTTP services)**
884
+ ```typescript
885
+ // ✅ Add CLS middleware configuration
886
+ ClsModule.forRoot({
887
+ middleware: {
888
+ mount: true,
889
+ generateId: true,
890
+ },
891
+ }),
892
+ ```
893
+
894
+ ### Reassurance: Nothing Breaks
895
+
896
+ **Good news**: Your existing logging calls will continue to work! The only changes needed are:
897
+
898
+ 1. ✅ Remove `app.useLogger()` and `app.flushLogs()` calls
899
+ 2. ✅ Add `LoggerModule.forRoot()` to your module imports
900
+ 3. ✅ Inject `LoggerService` where you need logging
901
+
902
+ **Logging behavior**:
903
+ - ✅ Still fire-and-forget (no `flushLogs()` needed)
904
+ - ✅ Still non-blocking
905
+ - ✅ Still failure-safe (errors are swallowed)
906
+ - ✅ Still works in all contexts (HTTP, Kafka, Bull, etc.)
907
+
908
+ **No breaking changes to logging API**:
909
+ - ✅ Same log levels: `debug`, `info`, `warn`, `error`, `fatal`, `emergency`
910
+ - ✅ Same method signatures
911
+ - ✅ Same `LogData` structure
912
+ - ✅ Same error handling
913
+
914
+ ### Example: Complete Migration
915
+
916
+ **Before (Deprecated)**:
917
+ ```typescript
918
+ import { NestFactory } from '@nestjs/core';
919
+ import { LoggerModule } from '@infineit/winston-logger';
920
+
921
+ @Module({
922
+ imports: [LoggerModule],
923
+ })
924
+ export class AppModule {}
925
+
926
+ async function bootstrap() {
927
+ const app = await NestFactory.create(AppModule);
928
+ app.useLogger(app.get(LoggerModule)); // ❌ Remove
929
+ await app.listen(3000);
930
+ app.flushLogs(); // ❌ Remove
931
+ }
932
+ ```
933
+
934
+ **After (Current)**:
935
+ ```typescript
936
+ import { NestFactory } from '@nestjs/core';
937
+ import { ClsModule } from 'nestjs-cls';
938
+ import { ContextModule, LoggerModule } from '@infineit/winston-logger';
939
+
940
+ @Module({
941
+ imports: [
942
+ ClsModule.forRoot({
943
+ middleware: { mount: true, generateId: true },
944
+ }),
945
+ ContextModule, // ✅ Add
946
+ LoggerModule.forRoot(), // ✅ Add
947
+ ],
948
+ })
949
+ export class AppModule {}
950
+
951
+ async function bootstrap() {
952
+ const app = await NestFactory.create(AppModule);
953
+ // ✅ No app.useLogger() needed
954
+ await app.listen(3000);
955
+ // ✅ No app.flushLogs() needed
956
+ }
957
+ ```
958
+
959
+ ### Need Help?
960
+
961
+ If you encounter issues during migration:
962
+
963
+ 1. **Check module imports**: Ensure `ContextModule` and `LoggerModule.forRoot()` are in your `AppModule`
964
+ 2. **Check CLS setup**: For HTTP services, ensure `ClsModule` is configured
965
+ 3. **Check injection**: Ensure `LoggerService` is injected via constructor
966
+ 4. **Review examples**: See the "Usage in Different Contexts" section above
967
+
968
+ ---
969
+
970
+ ## Best Practices
971
+
972
+ ### 1. Fire-and-Forget Logging
973
+
974
+ ✅ **DO**: Log without awaiting
975
+ ```typescript
976
+ this.logger.info('Processing started');
977
+ // Continue with business logic immediately
978
+ ```
979
+
980
+ ❌ **DON'T**: Try to await logging (methods return `void`)
981
+ ```typescript
982
+ // This won't work - logger methods return void
983
+ await this.logger.info('Message'); // ❌
984
+ ```
985
+
986
+ ### 2. Error Handling
987
+
988
+ ✅ **DO**: Pass Error objects directly
989
+ ```typescript
990
+ try {
991
+ // ...
992
+ } catch (error) {
993
+ this.logger.error(error, { userId: 123 });
994
+ }
995
+ ```
996
+
997
+ ✅ **DO**: Include context in error logs
998
+ ```typescript
999
+ this.logger.error(error, {
1000
+ userId: 123,
1001
+ orderId: 456,
1002
+ action: 'process-payment',
1003
+ });
1004
+ ```
1005
+
1006
+ ### 3. Structured Logging
1007
+
1008
+ ✅ **DO**: Use `props` for structured data
1009
+ ```typescript
1010
+ this.logger.info('User logged in', {
1011
+ props: {
1012
+ userId: 123,
1013
+ email: 'user@example.com',
1014
+ ipAddress: '192.168.1.1',
1015
+ },
1016
+ });
1017
+ ```
1018
+
1019
+ ### 4. Correlation ID
1020
+
1021
+ ✅ **DO**: Set correlation ID in non-HTTP contexts
1022
+ ```typescript
1023
+ // Kafka consumer
1024
+ this.contextStorage.setContextId(message.correlationId);
1025
+
1026
+ // Bull worker
1027
+ this.contextStorage.setContextId(job.id.toString());
1028
+ ```
1029
+
1030
+ ✅ **DO**: Use correlation ID for request tracing
1031
+ ```typescript
1032
+ // All logs in the same request/job will have the same correlationId
1033
+ this.logger.info('Step 1');
1034
+ this.logger.info('Step 2');
1035
+ // Both logs have the same correlationId
1036
+ ```
1037
+
1038
+ ### 5. Performance Logging
1039
+
1040
+ ✅ **DO**: Include duration for performance monitoring
1041
+ ```typescript
1042
+ const start = Date.now();
1043
+ await this.processData();
1044
+ this.logger.info('Processing completed', {
1045
+ durationMs: Date.now() - start,
1046
+ });
1047
+ ```
1048
+
1049
+ ---
1050
+
1051
+ ## Forbidden Actions
1052
+
1053
+ ### ❌ DO NOT: Add Database Dependencies
1054
+
1055
+ ```typescript
1056
+ // ❌ FORBIDDEN: Do not add Prisma, TypeORM, Mongoose, etc.
1057
+ import { PrismaClient } from '@prisma/client';
1058
+ ```
1059
+
1060
+ **Why**: The logger must work in all contexts without database assumptions.
1061
+
1062
+ ### ❌ DO NOT: Access HTTP Objects
1063
+
1064
+ ```typescript
1065
+ // ❌ FORBIDDEN: Do not inject HttpAdapterHost, Request, Response
1066
+ constructor(
1067
+ private readonly httpAdapter: HttpAdapterHost, // ❌
1068
+ ) {}
1069
+ ```
1070
+
1071
+ **Why**: The logger must work in non-HTTP contexts (Kafka, Bull, CRON, CLI).
1072
+
1073
+ ### ❌ DO NOT: Auto-Mount Middleware
1074
+
1075
+ ```typescript
1076
+ // ❌ FORBIDDEN: Do not implement NestModule or configure middleware
1077
+ export class LoggerModule implements NestModule { // ❌
1078
+ configure(consumer: MiddlewareConsumer) { // ❌
1079
+ consumer.apply(...).forRoutes('*');
1080
+ }
1081
+ }
1082
+ ```
1083
+
1084
+ **Why**: Applications must configure middleware themselves for flexibility.
1085
+
1086
+ ### ❌ DO NOT: Generate Correlation ID
1087
+
1088
+ ```typescript
1089
+ // ❌ FORBIDDEN: Do not generate correlation ID in logger
1090
+ import { v4 as uuidv4 } from 'uuid';
1091
+ const correlationId = uuidv4(); // ❌
1092
+ ```
1093
+
1094
+ **Why**: Correlation ID must come from CLS or be set by the application.
1095
+
1096
+ ### ❌ DO NOT: Throw Errors
1097
+
1098
+ ```typescript
1099
+ // ❌ FORBIDDEN: Do not throw errors in logging code
1100
+ if (!transport) {
1101
+ throw new Error('Transport not found'); // ❌
1102
+ }
1103
+ ```
1104
+
1105
+ **Why**: Logging failures must never break business logic. All errors are swallowed.
1106
+
1107
+ ### ❌ DO NOT: Use Async/Await in Logging
1108
+
1109
+ ```typescript
1110
+ // ❌ FORBIDDEN: Do not use async/await in logging
1111
+ async log(...) { // ❌
1112
+ await this.transport.send(...);
1113
+ }
1114
+ ```
1115
+
1116
+ **Why**: Logging must be fire-and-forget and non-blocking.
1117
+
1118
+ ---
1119
+
1120
+ ## Examples
1121
+
1122
+ ### Complete HTTP Service Example
1123
+
1124
+ ```typescript
1125
+ import { Module } from '@nestjs/common';
1126
+ import { ConfigModule } from '@nestjs/config';
1127
+ import { ClsModule } from 'nestjs-cls';
1128
+ import { v4 as uuidv4 } from 'uuid';
1129
+ import { ContextModule, LoggerModule } from '@infineit/winston-logger';
1130
+
1131
+ @Module({
1132
+ imports: [
1133
+ ConfigModule.forRoot({ isGlobal: true }),
1134
+ ClsModule.forRoot({
1135
+ middleware: {
1136
+ mount: true,
1137
+ generateId: true,
1138
+ idGenerator: (req: Request) =>
1139
+ req.headers['x-correlation-id'] as string || uuidv4(),
1140
+ },
1141
+ }),
1142
+ ContextModule,
1143
+ LoggerModule.forRoot({
1144
+ nodeEnv: process.env.NODE_ENV,
1145
+ organization: 'my-org',
1146
+ context: 'user-service',
1147
+ app: 'api',
1148
+ }),
1149
+ ],
1150
+ })
1151
+ export class AppModule {}
1152
+ ```
1153
+
1154
+ ```typescript
1155
+ import { Injectable } from '@nestjs/common';
1156
+ import { LoggerService } from '@infineit/winston-logger';
1157
+
1158
+ @Injectable()
1159
+ export class UserService {
1160
+ constructor(private readonly logger: LoggerService) {}
1161
+
1162
+ async findById(id: string) {
1163
+ this.logger.info('Fetching user', { userId: id });
1164
+
1165
+ try {
1166
+ const user = await this.repository.findById(id);
1167
+ this.logger.info('User found', { userId: id });
1168
+ return user;
1169
+ } catch (error) {
1170
+ this.logger.error(error, { userId: id });
1171
+ throw error;
1172
+ }
1173
+ }
1174
+ }
1175
+ ```
1176
+
1177
+ ### Complete Kafka Consumer Example
1178
+
1179
+ ```typescript
1180
+ import { Injectable } from '@nestjs/common';
1181
+ import { KafkaListener } from '@nestjs/microservices';
1182
+ import { LoggerService, ContextStorageService } from '@infineit/winston-logger';
1183
+
1184
+ @Injectable()
1185
+ export class OrderConsumer {
1186
+ constructor(
1187
+ private readonly logger: LoggerService,
1188
+ private readonly contextStorage: ContextStorageService,
1189
+ ) {}
1190
+
1191
+ @KafkaListener('order.created')
1192
+ async handleOrderCreated(message: OrderCreatedEvent) {
1193
+ // Set correlation ID from message
1194
+ this.contextStorage.setContextId(message.correlationId || message.id);
1195
+
1196
+ const start = Date.now();
1197
+ this.logger.info('Processing order', {
1198
+ orderId: message.id,
1199
+ userId: message.userId,
1200
+ });
1201
+
1202
+ try {
1203
+ await this.processOrder(message);
1204
+
1205
+ this.logger.info('Order processed', {
1206
+ orderId: message.id,
1207
+ durationMs: Date.now() - start,
1208
+ });
1209
+ } catch (error) {
1210
+ this.logger.error(error, {
1211
+ orderId: message.id,
1212
+ durationMs: Date.now() - start,
1213
+ });
1214
+ throw error;
1215
+ }
1216
+ }
1217
+ }
1218
+ ```
1219
+
1220
+ ### Custom Transport Example
1221
+
1222
+ ```typescript
1223
+ import { Injectable } from '@nestjs/common';
1224
+ import { LoggerTransport, NormalizedLog } from '@infineit/winston-logger';
1225
+ import { KafkaProducer } from './kafka-producer';
1226
+
1227
+ @Injectable()
1228
+ export class KafkaLogTransport implements LoggerTransport {
1229
+ constructor(private readonly kafkaProducer: KafkaProducer) {}
1230
+
1231
+ log(normalizedLog: NormalizedLog): void {
1232
+ try {
1233
+ // Fire-and-forget: send to Kafka without awaiting
1234
+ this.kafkaProducer.send({
1235
+ topic: 'application-logs',
1236
+ messages: [{
1237
+ key: normalizedLog.correlationId || 'no-correlation-id',
1238
+ value: JSON.stringify(normalizedLog),
1239
+ timestamp: normalizedLog.timestamp.toString(),
1240
+ }],
1241
+ });
1242
+ } catch (error) {
1243
+ // Swallow errors - never break business logic
1244
+ console.error('KafkaLogTransport error (swallowed):', error);
1245
+ }
1246
+ }
1247
+ }
1248
+ ```
1249
+
1250
+ ```typescript
1251
+ import { Provider } from '@nestjs/common';
1252
+ import { LoggerModule } from '@infineit/winston-logger';
1253
+ import { KafkaLogTransport } from './kafka-log-transport';
1254
+
1255
+ const customTransports: Provider[] = [
1256
+ {
1257
+ provide: KafkaLogTransport,
1258
+ useClass: KafkaLogTransport,
1259
+ },
1260
+ ];
1261
+
1262
+ @Module({
1263
+ imports: [
1264
+ LoggerModule.forRoot(config, customTransports),
1265
+ ],
1266
+ })
1267
+ export class AppModule {}
1268
+ ```
1269
+
1270
+ ---
159
1271
 
160
1272
  ## License
161
1273
 
162
- This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for more details.
1274
+ MIT
163
1275
 
164
1276
  ## Author
165
1277
 
166
- Dharmesh Patel 🇮🇳 <br>
167
- - [GitHub](https://github.com/dharmesh-r-patel/nestjs-starter)
168
- - [LinkedIn](https://www.linkedin.com/in/dharmeshbbay)
169
- - [Instagram](https://www.instagram.com/dharmesh_numbertank)
1278
+ Dharmesh Patel
1279
+
1280
+ ## Repository
1281
+
1282
+ https://github.com/dharmesh-r-patel/nestjs-logger
1283
+