@rawnodes/logger 1.6.0 → 1.8.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.
package/README.md CHANGED
@@ -1,16 +1,16 @@
1
1
  # @rawnodes/logger
2
2
 
3
- Flexible Winston-based logger with AsyncLocalStorage context, level overrides, and timing utilities.
3
+ Flexible Winston-based logger with AsyncLocalStorage context, level rules, and multiple output formats.
4
4
 
5
5
  ## Features
6
6
 
7
+ - **Multiple Formats** - JSON, plain (colored), logfmt, simple
7
8
  - **Context Propagation** - Automatic context via AsyncLocalStorage
8
- - **Level Overrides** - Debug specific users/modules without global log level change
9
+ - **Level Rules** - Configure log levels per module/context in config
9
10
  - **Lazy Meta** - Defer metadata creation until log level check passes
10
11
  - **Timing Utilities** - Built-in performance measurement
11
12
  - **Request ID** - Generate and extract request IDs
12
- - **Secret Masking** - Mask sensitive data in logs
13
- - **Singleton Factory** - Easy setup with `createSingletonLogger()`
13
+ - **Secret Masking** - Automatic masking of sensitive data
14
14
  - **TypeScript First** - Full generic type support
15
15
 
16
16
  ## Installation
@@ -23,241 +23,519 @@ npm install @rawnodes/logger
23
23
 
24
24
  ## Quick Start
25
25
 
26
- ### 1. Create your app logger
27
-
28
26
  ```typescript
29
- // src/logger/app.logger.ts
30
- import { createSingletonLogger, type LoggerContext } from '@rawnodes/logger';
27
+ import { Logger } from '@rawnodes/logger';
31
28
 
32
- export interface AppLoggerContext extends LoggerContext {
33
- userId?: number;
34
- requestId?: string;
35
- }
29
+ const logger = Logger.create({
30
+ level: 'info',
31
+ console: { format: 'plain' },
32
+ });
36
33
 
37
- export const AppLogger = createSingletonLogger<AppLoggerContext>();
34
+ logger.info('Hello world');
35
+ logger.info('User logged in', { userId: 123 });
38
36
  ```
39
37
 
40
- ### 2. Initialize on startup
38
+ ## Configuration
39
+
40
+ ### Basic Config
41
41
 
42
42
  ```typescript
43
- // src/main.ts
44
- import { AppLogger } from './logger/app.logger.js';
43
+ import { Logger } from '@rawnodes/logger';
45
44
 
46
- AppLogger.getInstance({
47
- level: 'info',
48
- console: { level: 'debug' },
49
- file: {
45
+ const logger = Logger.create({
46
+ level: 'info', // default log level
47
+ console: { format: 'plain' }, // console output format
48
+ file: { // optional file output
49
+ format: 'json',
50
50
  dirname: 'logs',
51
51
  filename: 'app-%DATE%.log',
52
- level: 'info',
53
52
  datePattern: 'YYYY-MM-DD',
54
53
  maxFiles: '14d',
54
+ maxSize: '20m',
55
55
  },
56
56
  });
57
57
  ```
58
58
 
59
- ### 3. Use in your code
59
+ ### Level Rules
60
+
61
+ Configure different log levels for specific modules or contexts:
60
62
 
61
63
  ```typescript
62
- import { AppLogger } from './logger/app.logger.js';
64
+ const logger = Logger.create({
65
+ level: {
66
+ default: 'info',
67
+ rules: [
68
+ { match: { context: 'auth' }, level: 'debug' }, // debug for auth module
69
+ { match: { context: 'database' }, level: 'warn' }, // warn for database
70
+ { match: { userId: 123 }, level: 'debug' }, // debug for user 123
71
+ { match: { context: 'api', userId: 456 }, level: 'silly' }, // combined match
72
+ ],
73
+ },
74
+ console: { format: 'plain' },
75
+ });
63
76
 
64
- const logger = AppLogger.for('UserService');
77
+ const authLogger = logger.for('auth');
78
+ authLogger.debug('This will be logged'); // matches rule
65
79
 
66
- logger.info('User created', { userId: 123 });
67
- logger.error('Failed to create user', { error });
80
+ const dbLogger = logger.for('database');
81
+ dbLogger.info('This will NOT be logged'); // level is warn
68
82
  ```
69
83
 
70
- ### 4. Add context in middleware
84
+ Rules from config are **readonly** and cannot be removed via API.
85
+
86
+ ### Output Formats
87
+
88
+ | Format | Description | Example |
89
+ |--------|-------------|---------|
90
+ | `json` | Structured JSON | `{"level":"info","message":"hello","timestamp":"..."}` |
91
+ | `plain` | Colored, human-readable | `[2025-01-01T12:00:00] info [APP] hello` |
92
+ | `logfmt` | Key=value pairs | `level=info msg=hello context=APP ts=2025-01-01T12:00:00` |
93
+ | `simple` | Minimal | `[2025-01-01T12:00:00] info: hello` |
71
94
 
72
95
  ```typescript
73
- // Express middleware
74
- app.use((req, res, next) => {
75
- const context: AppLoggerContext = {
76
- userId: req.user?.id,
77
- requestId: req.headers['x-request-id'] || generateRequestId(),
78
- };
79
- AppLogger.getStore().run(context, () => next());
96
+ // Different formats for console and file
97
+ const logger = Logger.create({
98
+ level: 'info',
99
+ console: { format: 'plain' }, // colored for development
100
+ file: {
101
+ format: 'json', // structured for log aggregation
102
+ dirname: 'logs',
103
+ filename: 'app-%DATE%.log',
104
+ },
80
105
  });
81
106
  ```
82
107
 
83
- ## Configuration
108
+ ### Per-Transport Level
109
+
110
+ Each transport can have its own log level:
84
111
 
85
112
  ```typescript
86
- interface LoggerConfig {
87
- level: string; // Default log level
113
+ const logger = Logger.create({
114
+ level: 'silly', // accept all at logger level
115
+ console: {
116
+ format: 'plain',
117
+ level: 'debug', // console: debug and above
118
+ },
119
+ file: {
120
+ format: 'json',
121
+ level: 'warn', // file: only warn and error
122
+ dirname: 'logs',
123
+ filename: 'app-%DATE%.log',
124
+ },
125
+ });
126
+ ```
127
+
128
+ | Scenario | Console | File |
129
+ |----------|---------|------|
130
+ | Development | `debug` | — |
131
+ | Production | `info` | `warn` |
132
+ | Troubleshooting | `debug` | `info` |
133
+ | Critical alerts | `info` | `error` |
134
+
135
+ This is useful for sending only critical errors to alerting systems while keeping verbose logs in console.
136
+
137
+ ### Per-Transport Rules
88
138
 
139
+ Each transport can have its own filtering rules. Use `level: 'off'` with rules to create a whitelist pattern:
140
+
141
+ ```typescript
142
+ const logger = Logger.create({
143
+ level: 'info',
144
+ console: { format: 'plain' },
145
+ file: {
146
+ format: 'json',
147
+ level: 'off', // off by default
148
+ rules: [
149
+ { match: { context: 'payments' }, level: 'error' }, // only errors from payments
150
+ { match: { context: 'auth' }, level: 'warn' }, // warn+ from auth
151
+ ],
152
+ dirname: 'logs',
153
+ filename: 'critical-%DATE%.log',
154
+ },
155
+ });
156
+
157
+ // Only error logs from 'payments' context go to file
158
+ logger.for('payments').error('Payment failed'); // → file
159
+ logger.for('payments').info('Processing'); // ✗ not logged to file
160
+ logger.for('other').error('Generic error'); // ✗ not logged to file (no matching rule)
161
+ ```
162
+
163
+ Rules can also match store context (AsyncLocalStorage):
164
+
165
+ ```typescript
166
+ const logger = Logger.create({
167
+ level: 'info',
168
+ console: { format: 'plain' },
169
+ file: {
170
+ format: 'json',
171
+ level: 'off',
172
+ rules: [
173
+ { match: { userId: 123 }, level: 'debug' }, // debug for specific user
174
+ { match: { context: 'api', premium: true }, level: 'debug' }, // debug for premium API users
175
+ ],
176
+ dirname: 'logs',
177
+ filename: 'debug-%DATE%.log',
178
+ },
179
+ });
180
+
181
+ // Logs for user 123 go to file
182
+ store.run({ userId: 123 }, () => {
183
+ logger.debug('User action'); // → file
184
+ });
185
+ ```
186
+
187
+ Use `level: 'off'` in rules to suppress specific contexts:
188
+
189
+ ```typescript
190
+ const logger = Logger.create({
191
+ level: 'debug',
89
192
  console: {
90
- level: string; // Console transport level
91
- };
193
+ format: 'plain',
194
+ level: 'debug',
195
+ rules: [
196
+ { match: { context: 'noisy-module' }, level: 'off' }, // suppress noisy logs
197
+ ],
198
+ },
199
+ });
92
200
 
93
- file?: {
94
- dirname: string; // Log directory
95
- filename: string; // Filename pattern (supports %DATE%)
96
- level: string; // File transport level
97
- datePattern: string; // Date pattern for rotation
98
- zippedArchive?: boolean;
99
- maxSize?: string; // e.g., '20m'
100
- maxFiles?: string; // e.g., '14d'
101
- };
201
+ logger.for('noisy-module').debug('Spam'); // ✗ not logged
202
+ logger.for('other').debug('Useful info'); // logged
203
+ ```
204
+
205
+ ## External Transports
206
+
207
+ ### Discord
208
+
209
+ Send logs to Discord via webhooks with rich embeds:
210
+
211
+ ```typescript
212
+ const logger = Logger.create({
213
+ level: 'info',
214
+ console: { format: 'plain' },
215
+ discord: {
216
+ webhookUrl: 'https://discord.com/api/webhooks/xxx/yyy',
217
+ level: 'error', // only errors to Discord
218
+ username: 'My App Logger', // optional bot name
219
+ avatarUrl: 'https://...', // optional avatar
220
+ embedColors: { // optional custom colors
221
+ error: 0xFF0000,
222
+ warn: 0xFFAA00,
223
+ },
224
+ batchSize: 10, // messages per batch (default: 10)
225
+ flushInterval: 2000, // flush interval ms (default: 2000)
226
+ },
227
+ });
228
+ ```
229
+
230
+ ### Telegram
231
+
232
+ Send logs to Telegram chats/channels:
233
+
234
+ ```typescript
235
+ const logger = Logger.create({
236
+ level: 'info',
237
+ console: { format: 'plain' },
238
+ telegram: {
239
+ botToken: process.env.TG_BOT_TOKEN!,
240
+ chatId: process.env.TG_CHAT_ID!,
241
+ level: 'warn', // warn and above
242
+ parseMode: 'Markdown', // 'Markdown' | 'MarkdownV2' | 'HTML'
243
+ disableNotification: false, // mute non-error by default
244
+ threadId: 123, // optional forum topic ID
245
+ replyToMessageId: 456, // optional reply to message
246
+ batchSize: 20, // default: 20
247
+ flushInterval: 1000, // default: 1000
248
+ },
249
+ });
250
+ ```
251
+
252
+ Use `level: 'off'` with rules for selective logging:
253
+
254
+ ```typescript
255
+ telegram: {
256
+ botToken: '...',
257
+ chatId: '...',
258
+ level: 'off', // off by default
259
+ rules: [
260
+ { match: { context: 'payments' }, level: 'error' }, // only payment errors
261
+ ],
102
262
  }
103
263
  ```
104
264
 
105
- ## Level Overrides
265
+ ### CloudWatch
106
266
 
107
- Debug specific users/modules without changing global log level:
267
+ Send logs to AWS CloudWatch Logs:
108
268
 
109
269
  ```typescript
110
- // Enable debug logging for user 123
111
- logger.setLevelOverride({ userId: 123 }, 'debug');
270
+ const logger = Logger.create({
271
+ level: 'info',
272
+ console: { format: 'plain' },
273
+ cloudwatch: {
274
+ logGroupName: '/app/my-service',
275
+ logStreamName: `${hostname}-${Date.now()}`,
276
+ region: 'us-east-1',
277
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
278
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
279
+ createLogGroup: true, // auto-create group (default: false)
280
+ createLogStream: true, // auto-create stream (default: true)
281
+ batchSize: 100, // default: 100
282
+ flushInterval: 1000, // default: 1000
283
+ },
284
+ });
285
+ ```
286
+
287
+ ### Transport Options
288
+
289
+ All external transports support:
290
+
291
+ | Option | Default | Description |
292
+ |--------|---------|-------------|
293
+ | `level` | — | Log level filter |
294
+ | `rules` | — | Per-transport filtering rules |
295
+ | `batchSize` | varies | Max messages per batch |
296
+ | `flushInterval` | varies | Flush interval in ms |
297
+ | `maxRetries` | 3 | Retry count on failure |
298
+ | `retryDelay` | 1000 | Base retry delay in ms |
299
+
300
+ ## Singleton Pattern
301
+
302
+ For app-wide logging:
303
+
304
+ ```typescript
305
+ // src/logger.ts
306
+ import { createSingletonLogger, type LoggerContext } from '@rawnodes/logger';
307
+
308
+ export interface AppContext extends LoggerContext {
309
+ userId?: number;
310
+ requestId?: string;
311
+ }
312
+
313
+ export const AppLogger = createSingletonLogger<AppContext>();
314
+
315
+ // src/main.ts
316
+ import { AppLogger } from './logger.js';
317
+
318
+ AppLogger.init({
319
+ level: {
320
+ default: 'info',
321
+ rules: [{ match: { context: 'debug-module' }, level: 'debug' }],
322
+ },
323
+ console: { format: 'plain' },
324
+ });
325
+
326
+ // Anywhere in your app
327
+ const logger = AppLogger.for('UserService');
328
+ logger.info('User created', { userId: 123 });
329
+ ```
330
+
331
+ ## Context Propagation
332
+
333
+ Automatically include context in all logs within an async scope:
334
+
335
+ ```typescript
336
+ // Express middleware
337
+ app.use((req, res, next) => {
338
+ const context = {
339
+ userId: req.user?.id,
340
+ requestId: req.headers['x-request-id'] || generateRequestId(),
341
+ };
342
+ AppLogger.getStore().run(context, () => next());
343
+ });
344
+
345
+ // All logs within this request will include userId and requestId
346
+ logger.info('Processing request');
347
+ // Output: [2025-01-01T12:00:00] info [APP] Processing request
348
+ // userId: 123
349
+ // requestId: abc-123
350
+ ```
112
351
 
113
- // Enable debug only for 'auth' module
114
- const authLogger = logger.child('auth');
115
- logger.setLevelOverride({ context: 'auth' }, 'debug');
352
+ ## Dynamic Level Overrides
116
353
 
117
- // Enable debug for specific user in specific module
118
- logger.setLevelOverride({ context: 'auth', userId: 123 }, 'debug');
354
+ Add/remove level overrides at runtime:
355
+
356
+ ```typescript
357
+ // Enable debug for specific user (e.g., for troubleshooting)
358
+ logger.setLevelOverride({ userId: 123 }, 'debug');
119
359
 
120
- // Now only auth module logs for user 123 will include debug level
121
- // Other users and modules still get the default level
360
+ // Enable debug for specific module
361
+ logger.setLevelOverride({ context: 'payments' }, 'debug');
122
362
 
123
- // Remove override when done
124
- logger.removeLevelOverride({ context: 'auth', userId: 123 });
363
+ // Remove override
364
+ logger.removeLevelOverride({ userId: 123 });
125
365
 
126
- // Or clear all overrides
366
+ // Clear all dynamic overrides (keeps config rules)
127
367
  logger.clearLevelOverrides();
368
+
369
+ // Get all overrides
370
+ const overrides = logger.getLevelOverrides();
371
+ // [{ match: { context: 'payments' }, level: 'debug', readonly: false }]
128
372
  ```
129
373
 
130
374
  ## Lazy Meta
131
375
 
132
- Avoid creating objects when log level doesn't allow logging:
376
+ Defer expensive object creation:
133
377
 
134
378
  ```typescript
135
- // Object is always created (even if debug is disabled)
136
- logger.debug('message', { heavy: computeExpensiveData() });
379
+ // BAD: Object created even if debug is disabled
380
+ logger.debug('Data processed', { result: expensiveSerialize(data) });
137
381
 
138
- // Function is only called when debug level is enabled
139
- logger.debug('message', () => ({ heavy: computeExpensiveData() }));
382
+ // GOOD: Function only called when debug is enabled
383
+ logger.debug('Data processed', () => ({ result: expensiveSerialize(data) }));
140
384
  ```
141
385
 
142
- This is useful for performance-critical code where you want debug logs but don't want to pay the cost of creating log metadata when debug is disabled.
143
-
144
- ## Timing
386
+ ## Child Loggers
145
387
 
146
- Measure execution time:
388
+ Create scoped loggers:
147
389
 
148
390
  ```typescript
149
- const logger = AppLogger.getInstance();
391
+ const logger = AppLogger.for('PaymentService');
392
+ logger.info('Processing payment');
393
+ // Output: [timestamp] info [PaymentService] Processing payment
394
+
395
+ const stripeLogger = logger.for('Stripe');
396
+ stripeLogger.info('Charging card');
397
+ // Output: [timestamp] info [Stripe] Charging card
398
+ ```
399
+
400
+ ## Utilities
401
+
402
+ ### Timing
150
403
 
151
- // Manual timing
152
- const timer = logger.time('database-query');
153
- await db.query('SELECT ...');
154
- logger.timeEnd(timer); // Logs: "database-query completed in 45.23ms"
404
+ ```typescript
405
+ import { measureAsync, measureSync } from '@rawnodes/logger';
155
406
 
156
- // Async wrapper
157
- const users = await logger.timeAsync('fetch-users', async () => {
407
+ // Async
408
+ const { result, timing } = await measureAsync('fetch-users', async () => {
158
409
  return await userService.findAll();
159
410
  });
411
+ console.log(timing); // { label: 'fetch-users', durationMs: 45.23, durationFormatted: '45.23ms' }
412
+
413
+ // Sync
414
+ const { result, timing } = measureSync('compute', () => {
415
+ return heavyComputation();
416
+ });
160
417
  ```
161
418
 
162
- ## Request ID Utilities
419
+ ### Request ID
163
420
 
164
421
  ```typescript
165
- import { generateRequestId, getOrGenerateRequestId } from '@rawnodes/logger';
422
+ import { generateRequestId, extractRequestId, getOrGenerateRequestId } from '@rawnodes/logger';
166
423
 
167
- // Generate new request ID
168
- const requestId = generateRequestId();
169
- // => "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
424
+ // Generate new
425
+ generateRequestId(); // "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
426
+ generateRequestId({ short: true }); // "a1b2c3d4"
427
+ generateRequestId({ prefix: 'req' }); // "req-a1b2c3d4-e5f6-..."
170
428
 
171
- // Short format
172
- const shortId = generateRequestId({ short: true });
173
- // => "a1b2c3d4"
429
+ // Extract from headers (checks x-request-id, x-correlation-id, x-trace-id)
430
+ extractRequestId(req.headers); // string | undefined
174
431
 
175
- // With prefix
176
- const prefixedId = generateRequestId({ prefix: 'req' });
177
- // => "req-a1b2c3d4-e5f6-..."
178
-
179
- // Extract from headers or generate new
180
- const id = getOrGenerateRequestId(req.headers);
432
+ // Extract or generate
433
+ getOrGenerateRequestId(req.headers); // always returns string
181
434
  ```
182
435
 
183
- ## Secret Masking
436
+ ### Secret Masking
437
+
438
+ Automatically masks sensitive fields in logs:
184
439
 
185
440
  ```typescript
186
- import { maskSecrets } from '@rawnodes/logger';
441
+ import { maskSecrets, createMasker } from '@rawnodes/logger';
187
442
 
188
- const masked = maskSecrets({
443
+ maskSecrets({
189
444
  user: 'admin',
190
445
  password: 'secret123',
191
- apiToken: 'tok_abc123xyz',
192
- url: 'postgres://user:pass@localhost/db',
446
+ apiKey: 'key_abc123',
193
447
  });
448
+ // { user: 'admin', password: '***', apiKey: '***' }
194
449
 
195
- // Result:
196
- // {
197
- // user: 'admin',
198
- // password: '***',
199
- // apiToken: '***',
200
- // url: 'postgres://user:***@localhost/db'
201
- // }
450
+ // Custom masker
451
+ const masker = createMasker({
452
+ patterns: ['ssn', 'creditCard'],
453
+ mask: '[REDACTED]'
454
+ });
202
455
  ```
203
456
 
204
- ## Child Loggers
457
+ **Default masked patterns:** `password`, `secret`, `token`, `apikey`, `api_key`, `api-key`, `auth`, `credential`, `private`
458
+
459
+ ## Logfmt Utilities
205
460
 
206
- Create scoped loggers with automatic context:
461
+ Helper functions for logfmt format:
207
462
 
208
463
  ```typescript
209
- // Get child logger with context name
210
- const logger = AppLogger.for('PaymentService');
211
- logger.info('Processing payment'); // [PaymentService] Processing payment
464
+ import { flattenObject, formatLogfmt, formatLogfmtValue } from '@rawnodes/logger';
465
+
466
+ flattenObject({ user: { id: 123, name: 'John' } });
467
+ // { 'user.id': 123, 'user.name': 'John' }
212
468
 
213
- // Or from instance
214
- const baseLogger = AppLogger.getInstance();
215
- const childLogger = baseLogger.getChildLogger('EmailService');
469
+ formatLogfmt({ level: 'info', msg: 'hello', userId: 123 });
470
+ // "level=info msg=hello userId=123"
216
471
  ```
217
472
 
218
473
  ## API Reference
219
474
 
220
- ### `createSingletonLogger<TContext>()`
221
-
222
- Creates a singleton logger factory.
475
+ ### Logger
223
476
 
224
- Returns:
225
477
  ```typescript
226
- interface SingletonLogger<TContext> {
227
- getInstance(config?: LoggerConfig): BaseLogger<TContext>;
228
- getStore(): LoggerStore<TContext>;
478
+ class Logger<TContext> {
479
+ static create(config: LoggerConfig, store?: LoggerStore): Logger;
480
+
229
481
  for(context: string): Logger;
230
- setLevelOverride(match: Partial<TContext>, level: string): void;
231
- removeLevelOverride(match: Partial<TContext>): void;
232
- getLevelOverrides(): LevelOverride<TContext>[];
482
+ getStore(): LoggerStore<TContext>;
483
+
484
+ // Logging
485
+ error(message: string, error?: Error, meta?: Meta): void;
486
+ warn(message: string, meta?: Meta): void;
487
+ info(message: string, meta?: Meta): void;
488
+ http(message: string, meta?: Meta): void;
489
+ verbose(message: string, meta?: Meta): void;
490
+ debug(message: string, meta?: Meta): void;
491
+ silly(message: string, meta?: Meta): void;
492
+
493
+ // Level overrides
494
+ setLevelOverride(match: LevelOverrideMatch, level: LogLevel): void;
495
+ removeLevelOverride(match: LevelOverrideMatch): boolean;
233
496
  clearLevelOverrides(): void;
497
+ getLevelOverrides(): LevelOverride[];
498
+
499
+ // Winston profiling
500
+ profile(id: string, meta?: object): void;
234
501
  }
235
502
  ```
236
503
 
237
- ### `BaseLogger<TContext>`
238
-
239
- Main logger class with methods:
240
- - `log(message, context?, meta?)`
241
- - `info(message, context?, meta?)`
242
- - `warn(message, context?, meta?)`
243
- - `error(message, error?, context?)`
244
- - `debug(message, context?, meta?)`
245
- - `verbose(message, context?, meta?)`
246
- - `time(label): Timer`
247
- - `timeEnd(timer, context?): TimingResult`
248
- - `timeAsync<T>(label, fn, context?): Promise<T>`
249
- - `getChildLogger(context): Logger`
250
- - `getStore(): LoggerStore<TContext>`
251
- - `setLevelOverride(match, level)`
252
- - `removeLevelOverride(match)`
253
- - `getLevelOverrides()`
254
- - `clearLevelOverrides()`
255
-
256
- ### `LoggerStore<TContext>`
257
-
258
- AsyncLocalStorage wrapper:
259
- - `getStore(): TContext | undefined`
260
- - `run<T>(context, fn): T`
504
+ ### Types
505
+
506
+ ```typescript
507
+ type LogLevel = 'error' | 'warn' | 'info' | 'http' | 'verbose' | 'debug' | 'silly' | 'off';
508
+ type LogFormat = 'json' | 'plain' | 'logfmt' | 'simple';
509
+ type Meta = object | (() => object);
510
+
511
+ interface LoggerConfig {
512
+ level: LogLevel | {
513
+ default: LogLevel;
514
+ rules?: LevelRule[];
515
+ };
516
+ console: {
517
+ format: LogFormat;
518
+ level?: LogLevel; // optional, overrides global level
519
+ rules?: LevelRule[]; // optional, per-transport filtering
520
+ };
521
+ file?: {
522
+ format: LogFormat;
523
+ level?: LogLevel; // optional, overrides global level
524
+ rules?: LevelRule[]; // optional, per-transport filtering
525
+ dirname: string;
526
+ filename: string;
527
+ datePattern?: string;
528
+ maxFiles?: string;
529
+ maxSize?: string;
530
+ zippedArchive?: boolean;
531
+ };
532
+ }
533
+
534
+ interface LevelRule {
535
+ match: Record<string, unknown> & { context?: string };
536
+ level: LogLevel;
537
+ }
538
+ ```
261
539
 
262
540
  ## Integration Examples
263
541
 
@@ -295,7 +573,7 @@ export class LoggerMiddleware implements NestMiddleware {
295
573
  }
296
574
  ```
297
575
 
298
- ### Telegraf (Telegram Bot)
576
+ ### Telegraf
299
577
 
300
578
  ```typescript
301
579
  import { Telegraf } from 'telegraf';