@rawnodes/logger 1.7.0 → 1.9.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
@@ -105,6 +105,231 @@ const logger = Logger.create({
105
105
  });
106
106
  ```
107
107
 
108
+ ### Per-Transport Level
109
+
110
+ Each transport can have its own log level:
111
+
112
+ ```typescript
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
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',
192
+ console: {
193
+ format: 'plain',
194
+ level: 'debug',
195
+ rules: [
196
+ { match: { context: 'noisy-module' }, level: 'off' }, // suppress noisy logs
197
+ ],
198
+ },
199
+ });
200
+
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
+ ],
262
+ }
263
+ ```
264
+
265
+ ### CloudWatch
266
+
267
+ Send logs to AWS CloudWatch Logs:
268
+
269
+ ```typescript
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
+ ### Multiple Transports
301
+
302
+ You can configure multiple instances of the same transport type using arrays:
303
+
304
+ ```typescript
305
+ const logger = Logger.create({
306
+ level: 'info',
307
+ console: { format: 'plain' },
308
+ discord: [
309
+ {
310
+ webhookUrl: 'https://discord.com/api/webhooks/payments/xxx',
311
+ level: 'off',
312
+ rules: [{ match: { context: 'payments' }, level: 'error' }],
313
+ },
314
+ {
315
+ webhookUrl: 'https://discord.com/api/webhooks/auth/yyy',
316
+ level: 'off',
317
+ rules: [{ match: { context: 'auth' }, level: 'error' }],
318
+ },
319
+ ],
320
+ telegram: [
321
+ { botToken: 'token1', chatId: 'errors-chat', level: 'error' },
322
+ { botToken: 'token2', chatId: 'alerts-chat', level: 'warn' },
323
+ ],
324
+ });
325
+
326
+ // Payment errors go to payments Discord channel
327
+ logger.for('payments').error('Payment failed');
328
+
329
+ // Auth errors go to auth Discord channel
330
+ logger.for('auth').error('Login failed');
331
+ ```
332
+
108
333
  ## Singleton Pattern
109
334
 
110
335
  For app-wide logging:
@@ -312,7 +537,7 @@ class Logger<TContext> {
312
537
  ### Types
313
538
 
314
539
  ```typescript
315
- type LogLevel = 'error' | 'warn' | 'info' | 'http' | 'verbose' | 'debug' | 'silly';
540
+ type LogLevel = 'error' | 'warn' | 'info' | 'http' | 'verbose' | 'debug' | 'silly' | 'off';
316
541
  type LogFormat = 'json' | 'plain' | 'logfmt' | 'simple';
317
542
  type Meta = object | (() => object);
318
543
 
@@ -321,9 +546,15 @@ interface LoggerConfig {
321
546
  default: LogLevel;
322
547
  rules?: LevelRule[];
323
548
  };
324
- console: { format: LogFormat };
549
+ console: {
550
+ format: LogFormat;
551
+ level?: LogLevel; // optional, overrides global level
552
+ rules?: LevelRule[]; // optional, per-transport filtering
553
+ };
325
554
  file?: {
326
555
  format: LogFormat;
556
+ level?: LogLevel; // optional, overrides global level
557
+ rules?: LevelRule[]; // optional, per-transport filtering
327
558
  dirname: string;
328
559
  filename: string;
329
560
  datePattern?: string;