@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 +233 -2
- package/dist/index.d.mts +898 -6
- package/dist/index.d.ts +898 -6
- package/dist/index.js +605 -22
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +585 -23
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -2
package/dist/index.js
CHANGED
|
@@ -4,11 +4,15 @@ var winston = require('winston');
|
|
|
4
4
|
var async_hooks = require('async_hooks');
|
|
5
5
|
var DailyRotateFile = require('winston-daily-rotate-file');
|
|
6
6
|
var util = require('util');
|
|
7
|
+
var TransportStream = require('winston-transport');
|
|
8
|
+
var clientCloudwatchLogs = require('@aws-sdk/client-cloudwatch-logs');
|
|
9
|
+
var zod = require('zod');
|
|
7
10
|
var crypto = require('crypto');
|
|
8
11
|
|
|
9
12
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
13
|
|
|
11
14
|
var DailyRotateFile__default = /*#__PURE__*/_interopDefault(DailyRotateFile);
|
|
15
|
+
var TransportStream__default = /*#__PURE__*/_interopDefault(TransportStream);
|
|
12
16
|
|
|
13
17
|
// src/state.ts
|
|
14
18
|
var LoggerStore = class {
|
|
@@ -21,6 +25,26 @@ var LoggerStore = class {
|
|
|
21
25
|
}
|
|
22
26
|
};
|
|
23
27
|
|
|
28
|
+
// src/types.ts
|
|
29
|
+
var LOG_LEVELS = {
|
|
30
|
+
off: -1,
|
|
31
|
+
error: 0,
|
|
32
|
+
warn: 1,
|
|
33
|
+
info: 2,
|
|
34
|
+
http: 3,
|
|
35
|
+
verbose: 4,
|
|
36
|
+
debug: 5,
|
|
37
|
+
silly: 6
|
|
38
|
+
};
|
|
39
|
+
function isValidLogLevel(level) {
|
|
40
|
+
return level in LOG_LEVELS;
|
|
41
|
+
}
|
|
42
|
+
function assertLogLevel(level) {
|
|
43
|
+
if (!isValidLogLevel(level)) {
|
|
44
|
+
throw new Error(`Invalid log level: "${level}". Valid levels: ${Object.keys(LOG_LEVELS).join(", ")}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
24
48
|
// src/utils/mask-secrets.ts
|
|
25
49
|
var DEFAULT_SECRET_PATTERNS = [
|
|
26
50
|
"password",
|
|
@@ -175,6 +199,20 @@ function maskSecretsFormat(options) {
|
|
|
175
199
|
return maskSecrets(info, options);
|
|
176
200
|
})();
|
|
177
201
|
}
|
|
202
|
+
function createFilterFormat(defaultLevel, rules, store) {
|
|
203
|
+
if (!rules?.length && defaultLevel === void 0) {
|
|
204
|
+
return winston.format((info) => info)();
|
|
205
|
+
}
|
|
206
|
+
return winston.format((info) => {
|
|
207
|
+
const logLevel = info.level;
|
|
208
|
+
const context = info.context;
|
|
209
|
+
const storeContext = store.getStore();
|
|
210
|
+
const matchingRule = rules?.find((rule) => matchesContext(storeContext, context, rule.match));
|
|
211
|
+
const effectiveLevel = matchingRule?.level ?? defaultLevel ?? "silly";
|
|
212
|
+
if (effectiveLevel === "off") return false;
|
|
213
|
+
return LOG_LEVELS[logLevel] <= LOG_LEVELS[effectiveLevel] ? info : false;
|
|
214
|
+
})();
|
|
215
|
+
}
|
|
178
216
|
function createPlainFormat(store) {
|
|
179
217
|
return winston.format.combine(
|
|
180
218
|
winston.format.errors({ stack: true }),
|
|
@@ -239,17 +277,461 @@ function createFormat(logFormat, store) {
|
|
|
239
277
|
}
|
|
240
278
|
}
|
|
241
279
|
|
|
280
|
+
// src/transports/buffer.ts
|
|
281
|
+
var MessageBuffer = class {
|
|
282
|
+
constructor(options) {
|
|
283
|
+
this.options = options;
|
|
284
|
+
}
|
|
285
|
+
queue = [];
|
|
286
|
+
timer = null;
|
|
287
|
+
flushing = false;
|
|
288
|
+
closed = false;
|
|
289
|
+
add(message) {
|
|
290
|
+
if (this.closed) return;
|
|
291
|
+
this.queue.push(message);
|
|
292
|
+
if (this.queue.length >= this.options.batchSize) {
|
|
293
|
+
void this.flush();
|
|
294
|
+
} else {
|
|
295
|
+
this.scheduleFlush();
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
async flush() {
|
|
299
|
+
if (this.flushing || this.queue.length === 0) return;
|
|
300
|
+
this.flushing = true;
|
|
301
|
+
this.clearTimer();
|
|
302
|
+
const messages = this.queue.splice(0, this.options.batchSize);
|
|
303
|
+
try {
|
|
304
|
+
await this.sendWithRetry(messages);
|
|
305
|
+
} catch (error) {
|
|
306
|
+
this.options.onError?.(error, messages);
|
|
307
|
+
} finally {
|
|
308
|
+
this.flushing = false;
|
|
309
|
+
if (this.queue.length > 0 && !this.closed) {
|
|
310
|
+
void this.flush();
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
async close() {
|
|
315
|
+
this.closed = true;
|
|
316
|
+
this.clearTimer();
|
|
317
|
+
while (this.queue.length > 0) {
|
|
318
|
+
await this.flush();
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
scheduleFlush() {
|
|
322
|
+
if (this.timer || this.closed) return;
|
|
323
|
+
this.timer = setTimeout(() => {
|
|
324
|
+
this.timer = null;
|
|
325
|
+
void this.flush();
|
|
326
|
+
}, this.options.flushInterval);
|
|
327
|
+
}
|
|
328
|
+
clearTimer() {
|
|
329
|
+
if (this.timer) {
|
|
330
|
+
clearTimeout(this.timer);
|
|
331
|
+
this.timer = null;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
async sendWithRetry(messages) {
|
|
335
|
+
let lastError = null;
|
|
336
|
+
for (let attempt = 0; attempt < this.options.maxRetries; attempt++) {
|
|
337
|
+
try {
|
|
338
|
+
await this.options.onFlush(messages);
|
|
339
|
+
return;
|
|
340
|
+
} catch (error) {
|
|
341
|
+
lastError = error;
|
|
342
|
+
if (attempt < this.options.maxRetries - 1) {
|
|
343
|
+
await this.delay(this.options.retryDelay * Math.pow(2, attempt));
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
throw lastError;
|
|
348
|
+
}
|
|
349
|
+
delay(ms) {
|
|
350
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
// src/transports/base-http.ts
|
|
355
|
+
var DEFAULT_OPTIONS = {
|
|
356
|
+
batchSize: 10,
|
|
357
|
+
flushInterval: 5e3,
|
|
358
|
+
maxRetries: 3,
|
|
359
|
+
retryDelay: 1e3
|
|
360
|
+
};
|
|
361
|
+
var BaseHttpTransport = class extends TransportStream__default.default {
|
|
362
|
+
buffer;
|
|
363
|
+
constructor(opts = {}) {
|
|
364
|
+
super();
|
|
365
|
+
this.buffer = new MessageBuffer({
|
|
366
|
+
batchSize: opts.batchSize ?? DEFAULT_OPTIONS.batchSize,
|
|
367
|
+
flushInterval: opts.flushInterval ?? DEFAULT_OPTIONS.flushInterval,
|
|
368
|
+
maxRetries: opts.maxRetries ?? DEFAULT_OPTIONS.maxRetries,
|
|
369
|
+
retryDelay: opts.retryDelay ?? DEFAULT_OPTIONS.retryDelay,
|
|
370
|
+
onFlush: this.sendBatch.bind(this),
|
|
371
|
+
onError: this.handleError.bind(this)
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
log(info, callback) {
|
|
375
|
+
const message = this.transformMessage(info);
|
|
376
|
+
this.buffer.add(message);
|
|
377
|
+
callback();
|
|
378
|
+
}
|
|
379
|
+
close() {
|
|
380
|
+
return this.buffer.close();
|
|
381
|
+
}
|
|
382
|
+
transformMessage(info) {
|
|
383
|
+
const { level, message, timestamp, context, ...meta } = info;
|
|
384
|
+
return {
|
|
385
|
+
level,
|
|
386
|
+
message: String(message),
|
|
387
|
+
timestamp: timestamp ? new Date(String(timestamp)) : /* @__PURE__ */ new Date(),
|
|
388
|
+
context,
|
|
389
|
+
meta: Object.keys(meta).length > 0 ? meta : void 0
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
handleError(error, messages) {
|
|
393
|
+
console.error(
|
|
394
|
+
`[${this.constructor.name}] Failed to send ${messages.length} messages:`,
|
|
395
|
+
error.message
|
|
396
|
+
);
|
|
397
|
+
this.emit("error", error);
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
// src/transports/discord.ts
|
|
402
|
+
var DEFAULT_EMBED_COLORS = {
|
|
403
|
+
off: 0,
|
|
404
|
+
error: 15548997,
|
|
405
|
+
warn: 16705372,
|
|
406
|
+
info: 5763719,
|
|
407
|
+
http: 5793266,
|
|
408
|
+
verbose: 10181046,
|
|
409
|
+
debug: 3447003,
|
|
410
|
+
silly: 9807270
|
|
411
|
+
};
|
|
412
|
+
var DiscordTransport = class extends BaseHttpTransport {
|
|
413
|
+
config;
|
|
414
|
+
constructor(config) {
|
|
415
|
+
super({
|
|
416
|
+
batchSize: config.batchSize ?? 10,
|
|
417
|
+
flushInterval: config.flushInterval ?? 2e3,
|
|
418
|
+
maxRetries: config.maxRetries,
|
|
419
|
+
retryDelay: config.retryDelay
|
|
420
|
+
});
|
|
421
|
+
this.config = config;
|
|
422
|
+
}
|
|
423
|
+
async sendBatch(messages) {
|
|
424
|
+
const chunks = this.chunkArray(messages, 10);
|
|
425
|
+
for (const chunk of chunks) {
|
|
426
|
+
const payload = {
|
|
427
|
+
username: this.config.username,
|
|
428
|
+
avatar_url: this.config.avatarUrl,
|
|
429
|
+
embeds: chunk.map((msg) => this.createEmbed(msg))
|
|
430
|
+
};
|
|
431
|
+
await this.sendWebhook(payload);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
createEmbed(msg) {
|
|
435
|
+
const color = this.config.embedColors?.[msg.level] ?? DEFAULT_EMBED_COLORS[msg.level];
|
|
436
|
+
const embed = {
|
|
437
|
+
title: `[${msg.level.toUpperCase()}] ${msg.context || "APP"}`,
|
|
438
|
+
description: msg.message.slice(0, 4096),
|
|
439
|
+
// Discord limit
|
|
440
|
+
color
|
|
441
|
+
};
|
|
442
|
+
if (this.config.includeTimestamp !== false) {
|
|
443
|
+
embed.timestamp = msg.timestamp.toISOString();
|
|
444
|
+
}
|
|
445
|
+
if (this.config.includeMeta !== false && msg.meta) {
|
|
446
|
+
embed.fields = this.metaToFields(msg.meta);
|
|
447
|
+
}
|
|
448
|
+
return embed;
|
|
449
|
+
}
|
|
450
|
+
metaToFields(meta) {
|
|
451
|
+
const maxFields = this.config.maxEmbedFields ?? 25;
|
|
452
|
+
const fields = [];
|
|
453
|
+
for (const [key, value] of Object.entries(meta)) {
|
|
454
|
+
if (fields.length >= maxFields) break;
|
|
455
|
+
let strValue;
|
|
456
|
+
if (typeof value === "object") {
|
|
457
|
+
strValue = "```json\n" + JSON.stringify(value, null, 2).slice(0, 1e3) + "\n```";
|
|
458
|
+
} else {
|
|
459
|
+
strValue = String(value).slice(0, 1024);
|
|
460
|
+
}
|
|
461
|
+
fields.push({
|
|
462
|
+
name: key.slice(0, 256),
|
|
463
|
+
value: strValue,
|
|
464
|
+
inline: typeof value !== "object" && String(value).length < 50
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
return fields;
|
|
468
|
+
}
|
|
469
|
+
async sendWebhook(payload) {
|
|
470
|
+
const response = await fetch(this.config.webhookUrl, {
|
|
471
|
+
method: "POST",
|
|
472
|
+
headers: { "Content-Type": "application/json" },
|
|
473
|
+
body: JSON.stringify(payload)
|
|
474
|
+
});
|
|
475
|
+
if (!response.ok) {
|
|
476
|
+
const text = await response.text();
|
|
477
|
+
throw new Error(`Discord webhook failed: ${response.status} ${text}`);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
chunkArray(array, size) {
|
|
481
|
+
const chunks = [];
|
|
482
|
+
for (let i = 0; i < array.length; i += size) {
|
|
483
|
+
chunks.push(array.slice(i, i + size));
|
|
484
|
+
}
|
|
485
|
+
return chunks;
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
// src/transports/telegram.ts
|
|
490
|
+
var LEVEL_EMOJI = {
|
|
491
|
+
off: "",
|
|
492
|
+
error: "\u{1F534}",
|
|
493
|
+
warn: "\u{1F7E1}",
|
|
494
|
+
info: "\u{1F7E2}",
|
|
495
|
+
http: "\u{1F535}",
|
|
496
|
+
verbose: "\u{1F7E3}",
|
|
497
|
+
debug: "\u26AA",
|
|
498
|
+
silly: "\u26AB"
|
|
499
|
+
};
|
|
500
|
+
var TelegramTransport = class extends BaseHttpTransport {
|
|
501
|
+
config;
|
|
502
|
+
apiUrl;
|
|
503
|
+
constructor(config) {
|
|
504
|
+
super({
|
|
505
|
+
batchSize: config.batchSize ?? 20,
|
|
506
|
+
flushInterval: config.flushInterval ?? 1e3,
|
|
507
|
+
maxRetries: config.maxRetries,
|
|
508
|
+
retryDelay: config.retryDelay
|
|
509
|
+
});
|
|
510
|
+
this.config = config;
|
|
511
|
+
this.apiUrl = `https://api.telegram.org/bot${config.botToken}`;
|
|
512
|
+
}
|
|
513
|
+
async sendBatch(messages) {
|
|
514
|
+
const text = this.formatBatchMessage(messages);
|
|
515
|
+
await this.sendMessage(text, messages);
|
|
516
|
+
}
|
|
517
|
+
formatBatchMessage(messages) {
|
|
518
|
+
const parseMode = this.config.parseMode ?? "Markdown";
|
|
519
|
+
return messages.map((msg) => {
|
|
520
|
+
if (parseMode === "HTML") {
|
|
521
|
+
return this.formatHtml(msg);
|
|
522
|
+
}
|
|
523
|
+
return this.formatMarkdown(msg, parseMode === "MarkdownV2");
|
|
524
|
+
}).join("\n\n---\n\n");
|
|
525
|
+
}
|
|
526
|
+
formatMarkdown(msg, v2) {
|
|
527
|
+
const emoji = LEVEL_EMOJI[msg.level];
|
|
528
|
+
const escape = v2 ? this.escapeMarkdownV2.bind(this) : (s) => s;
|
|
529
|
+
let text = `${emoji} *${msg.level.toUpperCase()}* \\[${escape(msg.context || "APP")}\\]
|
|
530
|
+
`;
|
|
531
|
+
text += escape(msg.message);
|
|
532
|
+
if (msg.meta && Object.keys(msg.meta).length > 0) {
|
|
533
|
+
const metaStr = JSON.stringify(msg.meta, null, 2);
|
|
534
|
+
text += "\n```json\n" + metaStr + "\n```";
|
|
535
|
+
}
|
|
536
|
+
return text;
|
|
537
|
+
}
|
|
538
|
+
formatHtml(msg) {
|
|
539
|
+
const emoji = LEVEL_EMOJI[msg.level];
|
|
540
|
+
let text = `${emoji} <b>${msg.level.toUpperCase()}</b> [${this.escapeHtml(msg.context || "APP")}]
|
|
541
|
+
`;
|
|
542
|
+
text += this.escapeHtml(msg.message);
|
|
543
|
+
if (msg.meta && Object.keys(msg.meta).length > 0) {
|
|
544
|
+
const metaStr = JSON.stringify(msg.meta, null, 2);
|
|
545
|
+
text += "\n<pre>" + this.escapeHtml(metaStr) + "</pre>";
|
|
546
|
+
}
|
|
547
|
+
return text;
|
|
548
|
+
}
|
|
549
|
+
shouldMute(messages) {
|
|
550
|
+
if (this.config.disableNotification !== void 0) {
|
|
551
|
+
return this.config.disableNotification;
|
|
552
|
+
}
|
|
553
|
+
return !messages.some((m) => m.level === "error");
|
|
554
|
+
}
|
|
555
|
+
async sendMessage(text, messages) {
|
|
556
|
+
const body = {
|
|
557
|
+
chat_id: this.config.chatId,
|
|
558
|
+
text,
|
|
559
|
+
parse_mode: this.config.parseMode ?? "Markdown",
|
|
560
|
+
disable_notification: this.shouldMute(messages)
|
|
561
|
+
};
|
|
562
|
+
if (this.config.threadId) {
|
|
563
|
+
body.message_thread_id = this.config.threadId;
|
|
564
|
+
}
|
|
565
|
+
if (this.config.replyToMessageId) {
|
|
566
|
+
body.reply_to_message_id = this.config.replyToMessageId;
|
|
567
|
+
}
|
|
568
|
+
const response = await fetch(`${this.apiUrl}/sendMessage`, {
|
|
569
|
+
method: "POST",
|
|
570
|
+
headers: { "Content-Type": "application/json" },
|
|
571
|
+
body: JSON.stringify(body)
|
|
572
|
+
});
|
|
573
|
+
if (!response.ok) {
|
|
574
|
+
const result = await response.json();
|
|
575
|
+
throw new Error(`Telegram API failed: ${response.status} ${JSON.stringify(result)}`);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
escapeMarkdownV2(text) {
|
|
579
|
+
return text.replace(/[_*[\]()~`>#+\-=|{}.!]/g, "\\$&");
|
|
580
|
+
}
|
|
581
|
+
escapeHtml(text) {
|
|
582
|
+
const entities = {
|
|
583
|
+
"&": "&",
|
|
584
|
+
"<": "<",
|
|
585
|
+
">": ">",
|
|
586
|
+
'"': """,
|
|
587
|
+
"'": "'"
|
|
588
|
+
};
|
|
589
|
+
return text.replace(/[&<>"']/g, (c) => entities[c] || c);
|
|
590
|
+
}
|
|
591
|
+
};
|
|
592
|
+
var CloudWatchTransport = class extends BaseHttpTransport {
|
|
593
|
+
config;
|
|
594
|
+
client;
|
|
595
|
+
sequenceToken;
|
|
596
|
+
initialized = false;
|
|
597
|
+
initPromise = null;
|
|
598
|
+
constructor(config) {
|
|
599
|
+
super({
|
|
600
|
+
batchSize: config.batchSize ?? 100,
|
|
601
|
+
flushInterval: config.flushInterval ?? 1e3,
|
|
602
|
+
maxRetries: config.maxRetries,
|
|
603
|
+
retryDelay: config.retryDelay
|
|
604
|
+
});
|
|
605
|
+
this.config = config;
|
|
606
|
+
this.client = new clientCloudwatchLogs.CloudWatchLogsClient({
|
|
607
|
+
region: config.region,
|
|
608
|
+
credentials: {
|
|
609
|
+
accessKeyId: config.accessKeyId,
|
|
610
|
+
secretAccessKey: config.secretAccessKey
|
|
611
|
+
}
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
async sendBatch(messages) {
|
|
615
|
+
await this.ensureInitialized();
|
|
616
|
+
const logEvents = messages.map((msg) => ({
|
|
617
|
+
timestamp: msg.timestamp.getTime(),
|
|
618
|
+
message: JSON.stringify({
|
|
619
|
+
level: msg.level,
|
|
620
|
+
message: msg.message,
|
|
621
|
+
context: msg.context,
|
|
622
|
+
...msg.meta
|
|
623
|
+
})
|
|
624
|
+
}));
|
|
625
|
+
logEvents.sort((a, b) => (a.timestamp ?? 0) - (b.timestamp ?? 0));
|
|
626
|
+
const command = new clientCloudwatchLogs.PutLogEventsCommand({
|
|
627
|
+
logGroupName: this.config.logGroupName,
|
|
628
|
+
logStreamName: this.config.logStreamName,
|
|
629
|
+
logEvents,
|
|
630
|
+
sequenceToken: this.sequenceToken
|
|
631
|
+
});
|
|
632
|
+
try {
|
|
633
|
+
const response = await this.client.send(command);
|
|
634
|
+
this.sequenceToken = response.nextSequenceToken;
|
|
635
|
+
} catch (error) {
|
|
636
|
+
if (this.isInvalidSequenceTokenError(error)) {
|
|
637
|
+
await this.fetchSequenceToken();
|
|
638
|
+
const retryCommand = new clientCloudwatchLogs.PutLogEventsCommand({
|
|
639
|
+
logGroupName: this.config.logGroupName,
|
|
640
|
+
logStreamName: this.config.logStreamName,
|
|
641
|
+
logEvents,
|
|
642
|
+
sequenceToken: this.sequenceToken
|
|
643
|
+
});
|
|
644
|
+
const response = await this.client.send(retryCommand);
|
|
645
|
+
this.sequenceToken = response.nextSequenceToken;
|
|
646
|
+
} else {
|
|
647
|
+
throw error;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
async ensureInitialized() {
|
|
652
|
+
if (this.initialized) return;
|
|
653
|
+
if (!this.initPromise) {
|
|
654
|
+
this.initPromise = this.initialize();
|
|
655
|
+
}
|
|
656
|
+
await this.initPromise;
|
|
657
|
+
}
|
|
658
|
+
async initialize() {
|
|
659
|
+
if (this.config.createLogGroup) {
|
|
660
|
+
await this.createLogGroupIfNotExists();
|
|
661
|
+
}
|
|
662
|
+
if (this.config.createLogStream !== false) {
|
|
663
|
+
await this.createLogStreamIfNotExists();
|
|
664
|
+
}
|
|
665
|
+
await this.fetchSequenceToken();
|
|
666
|
+
this.initialized = true;
|
|
667
|
+
}
|
|
668
|
+
async createLogGroupIfNotExists() {
|
|
669
|
+
try {
|
|
670
|
+
await this.client.send(
|
|
671
|
+
new clientCloudwatchLogs.CreateLogGroupCommand({
|
|
672
|
+
logGroupName: this.config.logGroupName
|
|
673
|
+
})
|
|
674
|
+
);
|
|
675
|
+
} catch (error) {
|
|
676
|
+
if (!this.isResourceAlreadyExistsError(error)) {
|
|
677
|
+
throw error;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
async createLogStreamIfNotExists() {
|
|
682
|
+
try {
|
|
683
|
+
await this.client.send(
|
|
684
|
+
new clientCloudwatchLogs.CreateLogStreamCommand({
|
|
685
|
+
logGroupName: this.config.logGroupName,
|
|
686
|
+
logStreamName: this.config.logStreamName
|
|
687
|
+
})
|
|
688
|
+
);
|
|
689
|
+
} catch (error) {
|
|
690
|
+
if (!this.isResourceAlreadyExistsError(error)) {
|
|
691
|
+
throw error;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
async fetchSequenceToken() {
|
|
696
|
+
const response = await this.client.send(
|
|
697
|
+
new clientCloudwatchLogs.DescribeLogStreamsCommand({
|
|
698
|
+
logGroupName: this.config.logGroupName,
|
|
699
|
+
logStreamNamePrefix: this.config.logStreamName,
|
|
700
|
+
limit: 1
|
|
701
|
+
})
|
|
702
|
+
);
|
|
703
|
+
const stream = response.logStreams?.find((s) => s.logStreamName === this.config.logStreamName);
|
|
704
|
+
this.sequenceToken = stream?.uploadSequenceToken;
|
|
705
|
+
}
|
|
706
|
+
isResourceAlreadyExistsError(error) {
|
|
707
|
+
return typeof error === "object" && error !== null && "name" in error && error.name === "ResourceAlreadyExistsException";
|
|
708
|
+
}
|
|
709
|
+
isInvalidSequenceTokenError(error) {
|
|
710
|
+
return typeof error === "object" && error !== null && "name" in error && error.name === "InvalidSequenceTokenException";
|
|
711
|
+
}
|
|
712
|
+
};
|
|
713
|
+
|
|
242
714
|
// src/transports.ts
|
|
715
|
+
function toArray(value) {
|
|
716
|
+
if (!value) return [];
|
|
717
|
+
return Array.isArray(value) ? value : [value];
|
|
718
|
+
}
|
|
243
719
|
function createTransports(config, store) {
|
|
244
720
|
const result = [
|
|
245
721
|
new winston.transports.Console({
|
|
246
|
-
format:
|
|
722
|
+
format: winston.format.combine(
|
|
723
|
+
createFilterFormat(config.console.level, config.console.rules, store),
|
|
724
|
+
createFormat(config.console.format, store)
|
|
725
|
+
)
|
|
247
726
|
})
|
|
248
727
|
];
|
|
249
728
|
if (config.file) {
|
|
250
729
|
result.push(
|
|
251
730
|
new DailyRotateFile__default.default({
|
|
252
|
-
format:
|
|
731
|
+
format: winston.format.combine(
|
|
732
|
+
createFilterFormat(config.file.level, config.file.rules, store),
|
|
733
|
+
createFormat(config.file.format, store)
|
|
734
|
+
),
|
|
253
735
|
dirname: config.file.dirname,
|
|
254
736
|
filename: config.file.filename,
|
|
255
737
|
datePattern: config.file.datePattern ?? "YYYY-MM-DD",
|
|
@@ -259,6 +741,30 @@ function createTransports(config, store) {
|
|
|
259
741
|
})
|
|
260
742
|
);
|
|
261
743
|
}
|
|
744
|
+
for (const discordConfig of toArray(config.discord)) {
|
|
745
|
+
const discord = new DiscordTransport(discordConfig);
|
|
746
|
+
discord.format = winston.format.combine(
|
|
747
|
+
createFilterFormat(discordConfig.level, discordConfig.rules, store),
|
|
748
|
+
winston.format.timestamp()
|
|
749
|
+
);
|
|
750
|
+
result.push(discord);
|
|
751
|
+
}
|
|
752
|
+
for (const telegramConfig of toArray(config.telegram)) {
|
|
753
|
+
const telegram = new TelegramTransport(telegramConfig);
|
|
754
|
+
telegram.format = winston.format.combine(
|
|
755
|
+
createFilterFormat(telegramConfig.level, telegramConfig.rules, store),
|
|
756
|
+
winston.format.timestamp()
|
|
757
|
+
);
|
|
758
|
+
result.push(telegram);
|
|
759
|
+
}
|
|
760
|
+
for (const cloudwatchConfig of toArray(config.cloudwatch)) {
|
|
761
|
+
const cloudwatch = new CloudWatchTransport(cloudwatchConfig);
|
|
762
|
+
cloudwatch.format = winston.format.combine(
|
|
763
|
+
createFilterFormat(cloudwatchConfig.level, cloudwatchConfig.rules, store),
|
|
764
|
+
winston.format.timestamp()
|
|
765
|
+
);
|
|
766
|
+
result.push(cloudwatch);
|
|
767
|
+
}
|
|
262
768
|
return result;
|
|
263
769
|
}
|
|
264
770
|
function createExceptionHandlers(config, store) {
|
|
@@ -283,25 +789,6 @@ function createExceptionHandlers(config, store) {
|
|
|
283
789
|
return result;
|
|
284
790
|
}
|
|
285
791
|
|
|
286
|
-
// src/types.ts
|
|
287
|
-
var LOG_LEVELS = {
|
|
288
|
-
error: 0,
|
|
289
|
-
warn: 1,
|
|
290
|
-
info: 2,
|
|
291
|
-
http: 3,
|
|
292
|
-
verbose: 4,
|
|
293
|
-
debug: 5,
|
|
294
|
-
silly: 6
|
|
295
|
-
};
|
|
296
|
-
function isValidLogLevel(level) {
|
|
297
|
-
return level in LOG_LEVELS;
|
|
298
|
-
}
|
|
299
|
-
function assertLogLevel(level) {
|
|
300
|
-
if (!isValidLogLevel(level)) {
|
|
301
|
-
throw new Error(`Invalid log level: "${level}". Valid levels: ${Object.keys(LOG_LEVELS).join(", ")}`);
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
792
|
// src/state.ts
|
|
306
793
|
function parseLevelConfig(level) {
|
|
307
794
|
if (typeof level === "string") {
|
|
@@ -356,6 +843,81 @@ function matchesContext(storeContext, loggerContext, match) {
|
|
|
356
843
|
const combined = { ...storeContext, context: loggerContext };
|
|
357
844
|
return Object.entries(match).every(([key, value]) => combined[key] === value);
|
|
358
845
|
}
|
|
846
|
+
var LogLevelSchema = zod.z.enum(["off", "error", "warn", "info", "http", "verbose", "debug", "silly"]);
|
|
847
|
+
var LogFormatSchema = zod.z.enum(["json", "plain", "logfmt", "simple"]);
|
|
848
|
+
var LevelRuleSchema = zod.z.object({
|
|
849
|
+
match: zod.z.record(zod.z.string(), zod.z.unknown()),
|
|
850
|
+
level: LogLevelSchema
|
|
851
|
+
});
|
|
852
|
+
var ConsoleConfigSchema = zod.z.object({
|
|
853
|
+
format: LogFormatSchema,
|
|
854
|
+
level: LogLevelSchema.optional(),
|
|
855
|
+
rules: zod.z.array(LevelRuleSchema).optional()
|
|
856
|
+
});
|
|
857
|
+
var FileConfigSchema = zod.z.object({
|
|
858
|
+
format: LogFormatSchema,
|
|
859
|
+
level: LogLevelSchema.optional(),
|
|
860
|
+
rules: zod.z.array(LevelRuleSchema).optional(),
|
|
861
|
+
dirname: zod.z.string().min(1, "dirname is required"),
|
|
862
|
+
filename: zod.z.string().min(1, "filename is required"),
|
|
863
|
+
datePattern: zod.z.string().optional(),
|
|
864
|
+
zippedArchive: zod.z.boolean().optional(),
|
|
865
|
+
maxSize: zod.z.string().optional(),
|
|
866
|
+
maxFiles: zod.z.string().optional()
|
|
867
|
+
});
|
|
868
|
+
var HttpTransportBaseConfigSchema = zod.z.object({
|
|
869
|
+
level: LogLevelSchema.optional(),
|
|
870
|
+
rules: zod.z.array(LevelRuleSchema).optional(),
|
|
871
|
+
batchSize: zod.z.number().int().positive().optional(),
|
|
872
|
+
flushInterval: zod.z.number().int().positive().optional(),
|
|
873
|
+
maxRetries: zod.z.number().int().nonnegative().optional(),
|
|
874
|
+
retryDelay: zod.z.number().int().positive().optional()
|
|
875
|
+
});
|
|
876
|
+
var DiscordConfigSchema = HttpTransportBaseConfigSchema.extend({
|
|
877
|
+
webhookUrl: zod.z.url("webhookUrl must be a valid URL"),
|
|
878
|
+
username: zod.z.string().optional(),
|
|
879
|
+
avatarUrl: zod.z.url().optional(),
|
|
880
|
+
embedColors: zod.z.record(zod.z.string(), zod.z.number().int()).optional(),
|
|
881
|
+
includeTimestamp: zod.z.boolean().optional(),
|
|
882
|
+
includeMeta: zod.z.boolean().optional(),
|
|
883
|
+
maxEmbedFields: zod.z.number().int().min(1).max(25).optional()
|
|
884
|
+
});
|
|
885
|
+
var TelegramConfigSchema = HttpTransportBaseConfigSchema.extend({
|
|
886
|
+
botToken: zod.z.string().min(1, "botToken is required"),
|
|
887
|
+
chatId: zod.z.union([zod.z.string(), zod.z.number()]),
|
|
888
|
+
parseMode: zod.z.enum(["Markdown", "MarkdownV2", "HTML"]).optional(),
|
|
889
|
+
disableNotification: zod.z.boolean().optional(),
|
|
890
|
+
threadId: zod.z.number().int().optional(),
|
|
891
|
+
replyToMessageId: zod.z.number().int().optional()
|
|
892
|
+
});
|
|
893
|
+
var CloudWatchConfigSchema = HttpTransportBaseConfigSchema.extend({
|
|
894
|
+
logGroupName: zod.z.string().min(1, "logGroupName is required"),
|
|
895
|
+
logStreamName: zod.z.string().min(1, "logStreamName is required"),
|
|
896
|
+
region: zod.z.string().min(1, "region is required"),
|
|
897
|
+
accessKeyId: zod.z.string().min(1, "accessKeyId is required"),
|
|
898
|
+
secretAccessKey: zod.z.string().min(1, "secretAccessKey is required"),
|
|
899
|
+
createLogGroup: zod.z.boolean().optional(),
|
|
900
|
+
createLogStream: zod.z.boolean().optional()
|
|
901
|
+
});
|
|
902
|
+
var LevelConfigObjectSchema = zod.z.object({
|
|
903
|
+
default: LogLevelSchema,
|
|
904
|
+
rules: zod.z.array(LevelRuleSchema).optional()
|
|
905
|
+
});
|
|
906
|
+
var LevelConfigSchema = zod.z.union([LogLevelSchema, LevelConfigObjectSchema]);
|
|
907
|
+
var LoggerConfigSchema = zod.z.object({
|
|
908
|
+
level: LevelConfigSchema,
|
|
909
|
+
console: ConsoleConfigSchema,
|
|
910
|
+
file: FileConfigSchema.optional(),
|
|
911
|
+
discord: zod.z.union([DiscordConfigSchema, zod.z.array(DiscordConfigSchema)]).optional(),
|
|
912
|
+
telegram: zod.z.union([TelegramConfigSchema, zod.z.array(TelegramConfigSchema)]).optional(),
|
|
913
|
+
cloudwatch: zod.z.union([CloudWatchConfigSchema, zod.z.array(CloudWatchConfigSchema)]).optional()
|
|
914
|
+
});
|
|
915
|
+
function validateConfig(config) {
|
|
916
|
+
return LoggerConfigSchema.parse(config);
|
|
917
|
+
}
|
|
918
|
+
function safeValidateConfig(config) {
|
|
919
|
+
return LoggerConfigSchema.safeParse(config);
|
|
920
|
+
}
|
|
359
921
|
|
|
360
922
|
// src/logger.ts
|
|
361
923
|
var Logger = class _Logger {
|
|
@@ -364,7 +926,8 @@ var Logger = class _Logger {
|
|
|
364
926
|
this.context = context;
|
|
365
927
|
}
|
|
366
928
|
static create(config, store) {
|
|
367
|
-
const
|
|
929
|
+
const validatedConfig = validateConfig(config);
|
|
930
|
+
const state = createState(validatedConfig, store);
|
|
368
931
|
return new _Logger(state, "APP");
|
|
369
932
|
}
|
|
370
933
|
for(context) {
|
|
@@ -544,9 +1107,26 @@ function getOrGenerateRequestId(headers, options = {}) {
|
|
|
544
1107
|
return extractRequestId(headers) ?? generateRequestId(options);
|
|
545
1108
|
}
|
|
546
1109
|
|
|
1110
|
+
exports.BaseHttpTransport = BaseHttpTransport;
|
|
1111
|
+
exports.CloudWatchConfigSchema = CloudWatchConfigSchema;
|
|
1112
|
+
exports.CloudWatchTransport = CloudWatchTransport;
|
|
1113
|
+
exports.ConsoleConfigSchema = ConsoleConfigSchema;
|
|
1114
|
+
exports.DiscordConfigSchema = DiscordConfigSchema;
|
|
1115
|
+
exports.DiscordTransport = DiscordTransport;
|
|
1116
|
+
exports.FileConfigSchema = FileConfigSchema;
|
|
1117
|
+
exports.HttpTransportBaseConfigSchema = HttpTransportBaseConfigSchema;
|
|
547
1118
|
exports.LOG_LEVELS = LOG_LEVELS;
|
|
1119
|
+
exports.LevelConfigObjectSchema = LevelConfigObjectSchema;
|
|
1120
|
+
exports.LevelConfigSchema = LevelConfigSchema;
|
|
1121
|
+
exports.LevelRuleSchema = LevelRuleSchema;
|
|
1122
|
+
exports.LogFormatSchema = LogFormatSchema;
|
|
1123
|
+
exports.LogLevelSchema = LogLevelSchema;
|
|
548
1124
|
exports.Logger = Logger;
|
|
1125
|
+
exports.LoggerConfigSchema = LoggerConfigSchema;
|
|
549
1126
|
exports.LoggerStore = LoggerStore;
|
|
1127
|
+
exports.MessageBuffer = MessageBuffer;
|
|
1128
|
+
exports.TelegramConfigSchema = TelegramConfigSchema;
|
|
1129
|
+
exports.TelegramTransport = TelegramTransport;
|
|
550
1130
|
exports.assertLogLevel = assertLogLevel;
|
|
551
1131
|
exports.createMasker = createMasker;
|
|
552
1132
|
exports.createSingletonLogger = createSingletonLogger;
|
|
@@ -558,7 +1138,10 @@ exports.generateRequestId = generateRequestId;
|
|
|
558
1138
|
exports.getOrGenerateRequestId = getOrGenerateRequestId;
|
|
559
1139
|
exports.isValidLogLevel = isValidLogLevel;
|
|
560
1140
|
exports.maskSecrets = maskSecrets;
|
|
1141
|
+
exports.matchesContext = matchesContext;
|
|
561
1142
|
exports.measureAsync = measureAsync;
|
|
562
1143
|
exports.measureSync = measureSync;
|
|
1144
|
+
exports.safeValidateConfig = safeValidateConfig;
|
|
1145
|
+
exports.validateConfig = validateConfig;
|
|
563
1146
|
//# sourceMappingURL=index.js.map
|
|
564
1147
|
//# sourceMappingURL=index.js.map
|