@rawnodes/logger 2.4.0 → 2.6.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 +147 -1
- package/dist/index.d.mts +67 -8
- package/dist/index.d.ts +67 -8
- package/dist/index.js +165 -17
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +160 -18
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -8,9 +8,9 @@ var promises = require('fs/promises');
|
|
|
8
8
|
var rotatingFileStream = require('rotating-file-stream');
|
|
9
9
|
var events = require('events');
|
|
10
10
|
var clientCloudwatchLogs = require('@aws-sdk/client-cloudwatch-logs');
|
|
11
|
+
var crypto = require('crypto');
|
|
11
12
|
var zod = require('zod');
|
|
12
13
|
var path = require('path');
|
|
13
|
-
var crypto = require('crypto');
|
|
14
14
|
|
|
15
15
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
16
16
|
|
|
@@ -422,13 +422,50 @@ var TelegramTransport = class extends BaseHttpTransport {
|
|
|
422
422
|
return text.replace(/[&<>"']/g, (c) => entities[c] || c);
|
|
423
423
|
}
|
|
424
424
|
};
|
|
425
|
+
var instanceUuid = crypto.randomUUID().slice(0, 8);
|
|
426
|
+
function formatDate() {
|
|
427
|
+
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
428
|
+
}
|
|
429
|
+
function formatDateTime() {
|
|
430
|
+
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace(/[T:]/g, "-");
|
|
431
|
+
}
|
|
432
|
+
function resolveLogStreamName(config, configHostname) {
|
|
433
|
+
const hostname = configHostname || process.env.HOSTNAME || os.hostname();
|
|
434
|
+
if (!config) {
|
|
435
|
+
return hostname;
|
|
436
|
+
}
|
|
437
|
+
if (typeof config === "string") {
|
|
438
|
+
return config;
|
|
439
|
+
}
|
|
440
|
+
if ("pattern" in config) {
|
|
441
|
+
switch (config.pattern) {
|
|
442
|
+
case "hostname":
|
|
443
|
+
return hostname;
|
|
444
|
+
case "hostname-date":
|
|
445
|
+
return `${hostname}/${formatDate()}`;
|
|
446
|
+
case "hostname-uuid":
|
|
447
|
+
return `${hostname}-${instanceUuid}`;
|
|
448
|
+
case "date":
|
|
449
|
+
return formatDate();
|
|
450
|
+
case "uuid":
|
|
451
|
+
return crypto.randomUUID();
|
|
452
|
+
default:
|
|
453
|
+
return hostname;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
if ("template" in config) {
|
|
457
|
+
return config.template.replace(/\{hostname\}/g, hostname).replace(/\{date\}/g, formatDate()).replace(/\{datetime\}/g, formatDateTime()).replace(/\{uuid\}/g, instanceUuid).replace(/\{pid\}/g, String(process.pid)).replace(/\{env\}/g, process.env.NODE_ENV || "development");
|
|
458
|
+
}
|
|
459
|
+
return hostname;
|
|
460
|
+
}
|
|
425
461
|
var CloudWatchTransport = class extends BaseHttpTransport {
|
|
426
462
|
config;
|
|
427
463
|
client;
|
|
428
464
|
sequenceToken;
|
|
429
465
|
initialized = false;
|
|
430
466
|
initPromise = null;
|
|
431
|
-
|
|
467
|
+
resolvedLogStreamName;
|
|
468
|
+
constructor(config, configHostname) {
|
|
432
469
|
super({
|
|
433
470
|
batchSize: config.batchSize ?? 100,
|
|
434
471
|
flushInterval: config.flushInterval ?? 1e3,
|
|
@@ -436,6 +473,7 @@ var CloudWatchTransport = class extends BaseHttpTransport {
|
|
|
436
473
|
retryDelay: config.retryDelay
|
|
437
474
|
});
|
|
438
475
|
this.config = config;
|
|
476
|
+
this.resolvedLogStreamName = resolveLogStreamName(config.logStreamName, configHostname);
|
|
439
477
|
this.client = new clientCloudwatchLogs.CloudWatchLogsClient({
|
|
440
478
|
region: config.region,
|
|
441
479
|
credentials: {
|
|
@@ -458,7 +496,7 @@ var CloudWatchTransport = class extends BaseHttpTransport {
|
|
|
458
496
|
logEvents.sort((a, b) => (a.timestamp ?? 0) - (b.timestamp ?? 0));
|
|
459
497
|
const command = new clientCloudwatchLogs.PutLogEventsCommand({
|
|
460
498
|
logGroupName: this.config.logGroupName,
|
|
461
|
-
logStreamName: this.
|
|
499
|
+
logStreamName: this.resolvedLogStreamName,
|
|
462
500
|
logEvents,
|
|
463
501
|
sequenceToken: this.sequenceToken
|
|
464
502
|
});
|
|
@@ -470,7 +508,7 @@ var CloudWatchTransport = class extends BaseHttpTransport {
|
|
|
470
508
|
await this.fetchSequenceToken();
|
|
471
509
|
const retryCommand = new clientCloudwatchLogs.PutLogEventsCommand({
|
|
472
510
|
logGroupName: this.config.logGroupName,
|
|
473
|
-
logStreamName: this.
|
|
511
|
+
logStreamName: this.resolvedLogStreamName,
|
|
474
512
|
logEvents,
|
|
475
513
|
sequenceToken: this.sequenceToken
|
|
476
514
|
});
|
|
@@ -516,7 +554,7 @@ var CloudWatchTransport = class extends BaseHttpTransport {
|
|
|
516
554
|
await this.client.send(
|
|
517
555
|
new clientCloudwatchLogs.CreateLogStreamCommand({
|
|
518
556
|
logGroupName: this.config.logGroupName,
|
|
519
|
-
logStreamName: this.
|
|
557
|
+
logStreamName: this.resolvedLogStreamName
|
|
520
558
|
})
|
|
521
559
|
);
|
|
522
560
|
} catch (error) {
|
|
@@ -529,11 +567,11 @@ var CloudWatchTransport = class extends BaseHttpTransport {
|
|
|
529
567
|
const response = await this.client.send(
|
|
530
568
|
new clientCloudwatchLogs.DescribeLogStreamsCommand({
|
|
531
569
|
logGroupName: this.config.logGroupName,
|
|
532
|
-
logStreamNamePrefix: this.
|
|
570
|
+
logStreamNamePrefix: this.resolvedLogStreamName,
|
|
533
571
|
limit: 1
|
|
534
572
|
})
|
|
535
573
|
);
|
|
536
|
-
const stream = response.logStreams?.find((s) => s.logStreamName === this.
|
|
574
|
+
const stream = response.logStreams?.find((s) => s.logStreamName === this.resolvedLogStreamName);
|
|
537
575
|
this.sequenceToken = stream?.uploadSequenceToken;
|
|
538
576
|
}
|
|
539
577
|
isResourceAlreadyExistsError(error) {
|
|
@@ -651,6 +689,7 @@ function createFormattedFilterStream(format, level, rules, store, destination) {
|
|
|
651
689
|
}
|
|
652
690
|
function createStreams(config, store) {
|
|
653
691
|
const streams = [];
|
|
692
|
+
const transports = [];
|
|
654
693
|
const consoleStream = createFormattedFilterStream(
|
|
655
694
|
config.console.format,
|
|
656
695
|
config.console.level,
|
|
@@ -698,6 +737,7 @@ function createStreams(config, store) {
|
|
|
698
737
|
}
|
|
699
738
|
for (const discordConfig of toArray(config.discord)) {
|
|
700
739
|
const transport = new DiscordTransport(discordConfig);
|
|
740
|
+
transports.push(transport);
|
|
701
741
|
const discordStream = createHttpTransportStream(transport, discordConfig.level, discordConfig.rules, store);
|
|
702
742
|
streams.push({
|
|
703
743
|
level: "trace",
|
|
@@ -706,6 +746,7 @@ function createStreams(config, store) {
|
|
|
706
746
|
}
|
|
707
747
|
for (const telegramConfig of toArray(config.telegram)) {
|
|
708
748
|
const transport = new TelegramTransport(telegramConfig);
|
|
749
|
+
transports.push(transport);
|
|
709
750
|
const telegramStream = createHttpTransportStream(transport, telegramConfig.level, telegramConfig.rules, store);
|
|
710
751
|
streams.push({
|
|
711
752
|
level: "trace",
|
|
@@ -713,14 +754,18 @@ function createStreams(config, store) {
|
|
|
713
754
|
});
|
|
714
755
|
}
|
|
715
756
|
for (const cloudwatchConfig of toArray(config.cloudwatch)) {
|
|
716
|
-
const transport = new CloudWatchTransport(cloudwatchConfig);
|
|
757
|
+
const transport = new CloudWatchTransport(cloudwatchConfig, config.hostname);
|
|
758
|
+
transports.push(transport);
|
|
717
759
|
const cwStream = createHttpTransportStream(transport, cloudwatchConfig.level, cloudwatchConfig.rules, store);
|
|
718
760
|
streams.push({
|
|
719
761
|
level: "trace",
|
|
720
762
|
stream: cwStream
|
|
721
763
|
});
|
|
722
764
|
}
|
|
723
|
-
return
|
|
765
|
+
return {
|
|
766
|
+
destination: pino__default.default.multistream(streams),
|
|
767
|
+
transports
|
|
768
|
+
};
|
|
724
769
|
}
|
|
725
770
|
function toArray(value) {
|
|
726
771
|
if (!value) return [];
|
|
@@ -805,14 +850,14 @@ function createState(config, store) {
|
|
|
805
850
|
levelOverrides.set(key, rule);
|
|
806
851
|
}
|
|
807
852
|
const { contextIndex, complexRules } = buildIndexes(levelOverrides);
|
|
808
|
-
const
|
|
853
|
+
const { destination, transports } = createStreams(config, loggerStore);
|
|
809
854
|
const options = {
|
|
810
855
|
level: "trace",
|
|
811
856
|
// Accept all, we filter in shouldLog()
|
|
812
857
|
customLevels: CUSTOM_LEVELS,
|
|
813
858
|
base: { hostname: config.hostname ?? os.hostname() }
|
|
814
859
|
};
|
|
815
|
-
const pinoLogger = pino__default.default(options,
|
|
860
|
+
const pinoLogger = pino__default.default(options, destination);
|
|
816
861
|
let callerConfig;
|
|
817
862
|
if (config.caller === true) {
|
|
818
863
|
callerConfig = {};
|
|
@@ -826,7 +871,8 @@ function createState(config, store) {
|
|
|
826
871
|
levelOverrides,
|
|
827
872
|
contextIndex,
|
|
828
873
|
complexRules,
|
|
829
|
-
callerConfig
|
|
874
|
+
callerConfig,
|
|
875
|
+
transports
|
|
830
876
|
};
|
|
831
877
|
}
|
|
832
878
|
function rebuildIndexes(state) {
|
|
@@ -927,6 +973,24 @@ var TelegramConfigSchema = zod.z.object({
|
|
|
927
973
|
threadId: zod.z.number().int().optional(),
|
|
928
974
|
replyToMessageId: zod.z.number().int().optional()
|
|
929
975
|
});
|
|
976
|
+
var LogStreamPatternSchema = zod.z.enum([
|
|
977
|
+
"hostname",
|
|
978
|
+
"hostname-date",
|
|
979
|
+
"hostname-uuid",
|
|
980
|
+
"date",
|
|
981
|
+
"uuid"
|
|
982
|
+
]);
|
|
983
|
+
var LogStreamPatternConfigSchema = zod.z.object({
|
|
984
|
+
pattern: LogStreamPatternSchema
|
|
985
|
+
});
|
|
986
|
+
var LogStreamTemplateConfigSchema = zod.z.object({
|
|
987
|
+
template: zod.z.string().min(1, "template is required")
|
|
988
|
+
});
|
|
989
|
+
var LogStreamNameSchema = zod.z.union([
|
|
990
|
+
zod.z.string().min(1, "logStreamName string must not be empty"),
|
|
991
|
+
LogStreamPatternConfigSchema,
|
|
992
|
+
LogStreamTemplateConfigSchema
|
|
993
|
+
]);
|
|
930
994
|
var CloudWatchConfigSchema = zod.z.object({
|
|
931
995
|
level: LogLevelSchema.optional(),
|
|
932
996
|
rules: zod.z.array(LevelRuleSchema).optional(),
|
|
@@ -935,7 +999,7 @@ var CloudWatchConfigSchema = zod.z.object({
|
|
|
935
999
|
maxRetries: zod.z.number().int().nonnegative().optional(),
|
|
936
1000
|
retryDelay: zod.z.number().int().positive().optional(),
|
|
937
1001
|
logGroupName: zod.z.string().min(1, "logGroupName is required"),
|
|
938
|
-
logStreamName:
|
|
1002
|
+
logStreamName: LogStreamNameSchema.optional(),
|
|
939
1003
|
region: zod.z.string().min(1, "region is required"),
|
|
940
1004
|
accessKeyId: zod.z.string().min(1, "accessKeyId is required"),
|
|
941
1005
|
secretAccessKey: zod.z.string().min(1, "secretAccessKey is required"),
|
|
@@ -954,6 +1018,10 @@ var CallerConfigSchema = zod.z.object({
|
|
|
954
1018
|
depth: zod.z.number().int().nonnegative().optional(),
|
|
955
1019
|
fullPath: zod.z.boolean().optional()
|
|
956
1020
|
});
|
|
1021
|
+
var AutoShutdownConfigSchema = zod.z.object({
|
|
1022
|
+
timeout: zod.z.number().int().positive().optional(),
|
|
1023
|
+
signals: zod.z.array(zod.z.string()).optional()
|
|
1024
|
+
});
|
|
957
1025
|
var LoggerConfigSchema = zod.z.object({
|
|
958
1026
|
level: LevelConfigSchema,
|
|
959
1027
|
console: ConsoleConfigSchema,
|
|
@@ -962,7 +1030,8 @@ var LoggerConfigSchema = zod.z.object({
|
|
|
962
1030
|
telegram: zod.z.union([TelegramConfigSchema, zod.z.array(TelegramConfigSchema)]).optional(),
|
|
963
1031
|
cloudwatch: zod.z.union([CloudWatchConfigSchema, zod.z.array(CloudWatchConfigSchema)]).optional(),
|
|
964
1032
|
caller: zod.z.union([zod.z.boolean(), CallerConfigSchema]).optional(),
|
|
965
|
-
hostname: zod.z.string().optional()
|
|
1033
|
+
hostname: zod.z.string().optional(),
|
|
1034
|
+
autoShutdown: zod.z.union([zod.z.boolean(), AutoShutdownConfigSchema]).optional()
|
|
966
1035
|
});
|
|
967
1036
|
function validateConfig(config) {
|
|
968
1037
|
return LoggerConfigSchema.parse(config);
|
|
@@ -1031,6 +1100,53 @@ function formatCallerInfo(info) {
|
|
|
1031
1100
|
return location;
|
|
1032
1101
|
}
|
|
1033
1102
|
|
|
1103
|
+
// src/utils/shutdown.ts
|
|
1104
|
+
var DEFAULT_OPTIONS2 = {
|
|
1105
|
+
timeout: 5e3,
|
|
1106
|
+
exitCode: 0,
|
|
1107
|
+
signals: ["SIGTERM", "SIGINT"]
|
|
1108
|
+
};
|
|
1109
|
+
var registered = false;
|
|
1110
|
+
function registerShutdown(logger, options = {}) {
|
|
1111
|
+
const opts = { ...DEFAULT_OPTIONS2, ...options };
|
|
1112
|
+
const shutdown = async (signal) => {
|
|
1113
|
+
if (signal) {
|
|
1114
|
+
console.error(`[Logger] Received ${signal}, shutting down gracefully...`);
|
|
1115
|
+
}
|
|
1116
|
+
try {
|
|
1117
|
+
if (opts.onShutdown) {
|
|
1118
|
+
await opts.onShutdown();
|
|
1119
|
+
}
|
|
1120
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
1121
|
+
setTimeout(() => {
|
|
1122
|
+
reject(new Error(`Shutdown timed out after ${opts.timeout}ms`));
|
|
1123
|
+
}, opts.timeout);
|
|
1124
|
+
});
|
|
1125
|
+
await Promise.race([
|
|
1126
|
+
logger.shutdown(),
|
|
1127
|
+
timeoutPromise
|
|
1128
|
+
]);
|
|
1129
|
+
if (signal) {
|
|
1130
|
+
console.error("[Logger] Graceful shutdown completed");
|
|
1131
|
+
process.exit(opts.exitCode);
|
|
1132
|
+
}
|
|
1133
|
+
} catch (error) {
|
|
1134
|
+
console.error("[Logger] Shutdown error:", error instanceof Error ? error.message : error);
|
|
1135
|
+
if (signal) {
|
|
1136
|
+
process.exit(1);
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
};
|
|
1140
|
+
if (!registered) {
|
|
1141
|
+
for (const signal of opts.signals) {
|
|
1142
|
+
process.on(signal, () => void shutdown(signal));
|
|
1143
|
+
}
|
|
1144
|
+
process.on("beforeExit", () => void shutdown());
|
|
1145
|
+
registered = true;
|
|
1146
|
+
}
|
|
1147
|
+
return shutdown;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1034
1150
|
// src/logger.ts
|
|
1035
1151
|
var Logger = class _Logger {
|
|
1036
1152
|
constructor(state, context) {
|
|
@@ -1041,7 +1157,12 @@ var Logger = class _Logger {
|
|
|
1041
1157
|
static create(config, store) {
|
|
1042
1158
|
const validatedConfig = validateConfig(config);
|
|
1043
1159
|
const state = createState(validatedConfig, store);
|
|
1044
|
-
|
|
1160
|
+
const logger = new _Logger(state, "APP");
|
|
1161
|
+
if (validatedConfig.autoShutdown) {
|
|
1162
|
+
const shutdownConfig = typeof validatedConfig.autoShutdown === "object" ? validatedConfig.autoShutdown : {};
|
|
1163
|
+
registerShutdown(logger, shutdownConfig);
|
|
1164
|
+
}
|
|
1165
|
+
return logger;
|
|
1045
1166
|
}
|
|
1046
1167
|
for(context) {
|
|
1047
1168
|
return new _Logger(this.state, context);
|
|
@@ -1083,6 +1204,15 @@ var Logger = class _Logger {
|
|
|
1083
1204
|
getLevelOverrides() {
|
|
1084
1205
|
return Array.from(this.state.levelOverrides.values());
|
|
1085
1206
|
}
|
|
1207
|
+
// Shutdown
|
|
1208
|
+
/**
|
|
1209
|
+
* Gracefully shutdown the logger, flushing all pending messages.
|
|
1210
|
+
* Should be called before process exit to ensure no logs are lost.
|
|
1211
|
+
*/
|
|
1212
|
+
async shutdown() {
|
|
1213
|
+
const closePromises = this.state.transports.map((transport) => transport.close());
|
|
1214
|
+
await Promise.all(closePromises);
|
|
1215
|
+
}
|
|
1086
1216
|
// Profiling
|
|
1087
1217
|
profile(id, meta) {
|
|
1088
1218
|
const existing = this.profileTimers.get(id);
|
|
@@ -1349,16 +1479,28 @@ function createMasker(options = {}) {
|
|
|
1349
1479
|
}
|
|
1350
1480
|
|
|
1351
1481
|
// src/formatters.ts
|
|
1352
|
-
|
|
1482
|
+
var MAX_FLATTEN_DEPTH = 10;
|
|
1483
|
+
var CIRCULAR_REF = "[Circular]";
|
|
1484
|
+
function flattenObject(obj, prefix = "", ancestors = /* @__PURE__ */ new WeakSet(), depth = 0) {
|
|
1353
1485
|
const result = {};
|
|
1486
|
+
if (depth > MAX_FLATTEN_DEPTH) {
|
|
1487
|
+
result[prefix || "value"] = "[Max depth exceeded]";
|
|
1488
|
+
return result;
|
|
1489
|
+
}
|
|
1490
|
+
ancestors.add(obj);
|
|
1354
1491
|
for (const [key, value] of Object.entries(obj)) {
|
|
1355
1492
|
const newKey = prefix ? `${prefix}.${key}` : key;
|
|
1356
1493
|
if (value !== null && typeof value === "object" && !Array.isArray(value) && !(value instanceof Error)) {
|
|
1357
|
-
|
|
1494
|
+
if (ancestors.has(value)) {
|
|
1495
|
+
result[newKey] = CIRCULAR_REF;
|
|
1496
|
+
} else {
|
|
1497
|
+
Object.assign(result, flattenObject(value, newKey, ancestors, depth + 1));
|
|
1498
|
+
}
|
|
1358
1499
|
} else {
|
|
1359
1500
|
result[newKey] = value;
|
|
1360
1501
|
}
|
|
1361
1502
|
}
|
|
1503
|
+
ancestors.delete(obj);
|
|
1362
1504
|
return result;
|
|
1363
1505
|
}
|
|
1364
1506
|
function formatLogfmtValue(value) {
|
|
@@ -1387,6 +1529,7 @@ function formatLogfmt(data) {
|
|
|
1387
1529
|
return Object.entries(flattened).filter(([, value]) => value !== void 0 && value !== null).map(([key, value]) => `${key}=${formatLogfmtValue(value)}`).join(" ");
|
|
1388
1530
|
}
|
|
1389
1531
|
|
|
1532
|
+
exports.AutoShutdownConfigSchema = AutoShutdownConfigSchema;
|
|
1390
1533
|
exports.BaseHttpTransport = BaseHttpTransport;
|
|
1391
1534
|
exports.CallerConfigSchema = CallerConfigSchema;
|
|
1392
1535
|
exports.CloudWatchConfigSchema = CloudWatchConfigSchema;
|
|
@@ -1402,6 +1545,10 @@ exports.LevelConfigSchema = LevelConfigSchema;
|
|
|
1402
1545
|
exports.LevelRuleSchema = LevelRuleSchema;
|
|
1403
1546
|
exports.LogFormatSchema = LogFormatSchema;
|
|
1404
1547
|
exports.LogLevelSchema = LogLevelSchema;
|
|
1548
|
+
exports.LogStreamNameSchema = LogStreamNameSchema;
|
|
1549
|
+
exports.LogStreamPatternConfigSchema = LogStreamPatternConfigSchema;
|
|
1550
|
+
exports.LogStreamPatternSchema = LogStreamPatternSchema;
|
|
1551
|
+
exports.LogStreamTemplateConfigSchema = LogStreamTemplateConfigSchema;
|
|
1405
1552
|
exports.Logger = Logger;
|
|
1406
1553
|
exports.LoggerConfigSchema = LoggerConfigSchema;
|
|
1407
1554
|
exports.LoggerStore = LoggerStore;
|
|
@@ -1424,6 +1571,7 @@ exports.maskSecrets = maskSecrets;
|
|
|
1424
1571
|
exports.matchesContext = matchesContext;
|
|
1425
1572
|
exports.measureAsync = measureAsync;
|
|
1426
1573
|
exports.measureSync = measureSync;
|
|
1574
|
+
exports.registerShutdown = registerShutdown;
|
|
1427
1575
|
exports.safeValidateConfig = safeValidateConfig;
|
|
1428
1576
|
exports.validateConfig = validateConfig;
|
|
1429
1577
|
//# sourceMappingURL=index.js.map
|