@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.mjs
CHANGED
|
@@ -6,9 +6,9 @@ import { mkdir } from 'fs/promises';
|
|
|
6
6
|
import { createStream } from 'rotating-file-stream';
|
|
7
7
|
import { EventEmitter } from 'events';
|
|
8
8
|
import { CloudWatchLogsClient, PutLogEventsCommand, CreateLogGroupCommand, CreateLogStreamCommand, DescribeLogStreamsCommand } from '@aws-sdk/client-cloudwatch-logs';
|
|
9
|
+
import { randomUUID } from 'crypto';
|
|
9
10
|
import { z } from 'zod';
|
|
10
11
|
import { relative, basename } from 'path';
|
|
11
|
-
import { randomUUID } from 'crypto';
|
|
12
12
|
|
|
13
13
|
// src/state.ts
|
|
14
14
|
var LoggerStore = class {
|
|
@@ -416,13 +416,50 @@ var TelegramTransport = class extends BaseHttpTransport {
|
|
|
416
416
|
return text.replace(/[&<>"']/g, (c) => entities[c] || c);
|
|
417
417
|
}
|
|
418
418
|
};
|
|
419
|
+
var instanceUuid = randomUUID().slice(0, 8);
|
|
420
|
+
function formatDate() {
|
|
421
|
+
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
422
|
+
}
|
|
423
|
+
function formatDateTime() {
|
|
424
|
+
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace(/[T:]/g, "-");
|
|
425
|
+
}
|
|
426
|
+
function resolveLogStreamName(config, configHostname) {
|
|
427
|
+
const hostname$1 = configHostname || process.env.HOSTNAME || hostname();
|
|
428
|
+
if (!config) {
|
|
429
|
+
return hostname$1;
|
|
430
|
+
}
|
|
431
|
+
if (typeof config === "string") {
|
|
432
|
+
return config;
|
|
433
|
+
}
|
|
434
|
+
if ("pattern" in config) {
|
|
435
|
+
switch (config.pattern) {
|
|
436
|
+
case "hostname":
|
|
437
|
+
return hostname$1;
|
|
438
|
+
case "hostname-date":
|
|
439
|
+
return `${hostname$1}/${formatDate()}`;
|
|
440
|
+
case "hostname-uuid":
|
|
441
|
+
return `${hostname$1}-${instanceUuid}`;
|
|
442
|
+
case "date":
|
|
443
|
+
return formatDate();
|
|
444
|
+
case "uuid":
|
|
445
|
+
return randomUUID();
|
|
446
|
+
default:
|
|
447
|
+
return hostname$1;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
if ("template" in config) {
|
|
451
|
+
return config.template.replace(/\{hostname\}/g, hostname$1).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");
|
|
452
|
+
}
|
|
453
|
+
return hostname$1;
|
|
454
|
+
}
|
|
419
455
|
var CloudWatchTransport = class extends BaseHttpTransport {
|
|
420
456
|
config;
|
|
421
457
|
client;
|
|
422
458
|
sequenceToken;
|
|
423
459
|
initialized = false;
|
|
424
460
|
initPromise = null;
|
|
425
|
-
|
|
461
|
+
resolvedLogStreamName;
|
|
462
|
+
constructor(config, configHostname) {
|
|
426
463
|
super({
|
|
427
464
|
batchSize: config.batchSize ?? 100,
|
|
428
465
|
flushInterval: config.flushInterval ?? 1e3,
|
|
@@ -430,6 +467,7 @@ var CloudWatchTransport = class extends BaseHttpTransport {
|
|
|
430
467
|
retryDelay: config.retryDelay
|
|
431
468
|
});
|
|
432
469
|
this.config = config;
|
|
470
|
+
this.resolvedLogStreamName = resolveLogStreamName(config.logStreamName, configHostname);
|
|
433
471
|
this.client = new CloudWatchLogsClient({
|
|
434
472
|
region: config.region,
|
|
435
473
|
credentials: {
|
|
@@ -452,7 +490,7 @@ var CloudWatchTransport = class extends BaseHttpTransport {
|
|
|
452
490
|
logEvents.sort((a, b) => (a.timestamp ?? 0) - (b.timestamp ?? 0));
|
|
453
491
|
const command = new PutLogEventsCommand({
|
|
454
492
|
logGroupName: this.config.logGroupName,
|
|
455
|
-
logStreamName: this.
|
|
493
|
+
logStreamName: this.resolvedLogStreamName,
|
|
456
494
|
logEvents,
|
|
457
495
|
sequenceToken: this.sequenceToken
|
|
458
496
|
});
|
|
@@ -464,7 +502,7 @@ var CloudWatchTransport = class extends BaseHttpTransport {
|
|
|
464
502
|
await this.fetchSequenceToken();
|
|
465
503
|
const retryCommand = new PutLogEventsCommand({
|
|
466
504
|
logGroupName: this.config.logGroupName,
|
|
467
|
-
logStreamName: this.
|
|
505
|
+
logStreamName: this.resolvedLogStreamName,
|
|
468
506
|
logEvents,
|
|
469
507
|
sequenceToken: this.sequenceToken
|
|
470
508
|
});
|
|
@@ -510,7 +548,7 @@ var CloudWatchTransport = class extends BaseHttpTransport {
|
|
|
510
548
|
await this.client.send(
|
|
511
549
|
new CreateLogStreamCommand({
|
|
512
550
|
logGroupName: this.config.logGroupName,
|
|
513
|
-
logStreamName: this.
|
|
551
|
+
logStreamName: this.resolvedLogStreamName
|
|
514
552
|
})
|
|
515
553
|
);
|
|
516
554
|
} catch (error) {
|
|
@@ -523,11 +561,11 @@ var CloudWatchTransport = class extends BaseHttpTransport {
|
|
|
523
561
|
const response = await this.client.send(
|
|
524
562
|
new DescribeLogStreamsCommand({
|
|
525
563
|
logGroupName: this.config.logGroupName,
|
|
526
|
-
logStreamNamePrefix: this.
|
|
564
|
+
logStreamNamePrefix: this.resolvedLogStreamName,
|
|
527
565
|
limit: 1
|
|
528
566
|
})
|
|
529
567
|
);
|
|
530
|
-
const stream = response.logStreams?.find((s) => s.logStreamName === this.
|
|
568
|
+
const stream = response.logStreams?.find((s) => s.logStreamName === this.resolvedLogStreamName);
|
|
531
569
|
this.sequenceToken = stream?.uploadSequenceToken;
|
|
532
570
|
}
|
|
533
571
|
isResourceAlreadyExistsError(error) {
|
|
@@ -645,6 +683,7 @@ function createFormattedFilterStream(format, level, rules, store, destination) {
|
|
|
645
683
|
}
|
|
646
684
|
function createStreams(config, store) {
|
|
647
685
|
const streams = [];
|
|
686
|
+
const transports = [];
|
|
648
687
|
const consoleStream = createFormattedFilterStream(
|
|
649
688
|
config.console.format,
|
|
650
689
|
config.console.level,
|
|
@@ -692,6 +731,7 @@ function createStreams(config, store) {
|
|
|
692
731
|
}
|
|
693
732
|
for (const discordConfig of toArray(config.discord)) {
|
|
694
733
|
const transport = new DiscordTransport(discordConfig);
|
|
734
|
+
transports.push(transport);
|
|
695
735
|
const discordStream = createHttpTransportStream(transport, discordConfig.level, discordConfig.rules, store);
|
|
696
736
|
streams.push({
|
|
697
737
|
level: "trace",
|
|
@@ -700,6 +740,7 @@ function createStreams(config, store) {
|
|
|
700
740
|
}
|
|
701
741
|
for (const telegramConfig of toArray(config.telegram)) {
|
|
702
742
|
const transport = new TelegramTransport(telegramConfig);
|
|
743
|
+
transports.push(transport);
|
|
703
744
|
const telegramStream = createHttpTransportStream(transport, telegramConfig.level, telegramConfig.rules, store);
|
|
704
745
|
streams.push({
|
|
705
746
|
level: "trace",
|
|
@@ -707,14 +748,18 @@ function createStreams(config, store) {
|
|
|
707
748
|
});
|
|
708
749
|
}
|
|
709
750
|
for (const cloudwatchConfig of toArray(config.cloudwatch)) {
|
|
710
|
-
const transport = new CloudWatchTransport(cloudwatchConfig);
|
|
751
|
+
const transport = new CloudWatchTransport(cloudwatchConfig, config.hostname);
|
|
752
|
+
transports.push(transport);
|
|
711
753
|
const cwStream = createHttpTransportStream(transport, cloudwatchConfig.level, cloudwatchConfig.rules, store);
|
|
712
754
|
streams.push({
|
|
713
755
|
level: "trace",
|
|
714
756
|
stream: cwStream
|
|
715
757
|
});
|
|
716
758
|
}
|
|
717
|
-
return
|
|
759
|
+
return {
|
|
760
|
+
destination: pino.multistream(streams),
|
|
761
|
+
transports
|
|
762
|
+
};
|
|
718
763
|
}
|
|
719
764
|
function toArray(value) {
|
|
720
765
|
if (!value) return [];
|
|
@@ -799,14 +844,14 @@ function createState(config, store) {
|
|
|
799
844
|
levelOverrides.set(key, rule);
|
|
800
845
|
}
|
|
801
846
|
const { contextIndex, complexRules } = buildIndexes(levelOverrides);
|
|
802
|
-
const
|
|
847
|
+
const { destination, transports } = createStreams(config, loggerStore);
|
|
803
848
|
const options = {
|
|
804
849
|
level: "trace",
|
|
805
850
|
// Accept all, we filter in shouldLog()
|
|
806
851
|
customLevels: CUSTOM_LEVELS,
|
|
807
852
|
base: { hostname: config.hostname ?? hostname() }
|
|
808
853
|
};
|
|
809
|
-
const pinoLogger = pino(options,
|
|
854
|
+
const pinoLogger = pino(options, destination);
|
|
810
855
|
let callerConfig;
|
|
811
856
|
if (config.caller === true) {
|
|
812
857
|
callerConfig = {};
|
|
@@ -820,7 +865,8 @@ function createState(config, store) {
|
|
|
820
865
|
levelOverrides,
|
|
821
866
|
contextIndex,
|
|
822
867
|
complexRules,
|
|
823
|
-
callerConfig
|
|
868
|
+
callerConfig,
|
|
869
|
+
transports
|
|
824
870
|
};
|
|
825
871
|
}
|
|
826
872
|
function rebuildIndexes(state) {
|
|
@@ -921,6 +967,24 @@ var TelegramConfigSchema = z.object({
|
|
|
921
967
|
threadId: z.number().int().optional(),
|
|
922
968
|
replyToMessageId: z.number().int().optional()
|
|
923
969
|
});
|
|
970
|
+
var LogStreamPatternSchema = z.enum([
|
|
971
|
+
"hostname",
|
|
972
|
+
"hostname-date",
|
|
973
|
+
"hostname-uuid",
|
|
974
|
+
"date",
|
|
975
|
+
"uuid"
|
|
976
|
+
]);
|
|
977
|
+
var LogStreamPatternConfigSchema = z.object({
|
|
978
|
+
pattern: LogStreamPatternSchema
|
|
979
|
+
});
|
|
980
|
+
var LogStreamTemplateConfigSchema = z.object({
|
|
981
|
+
template: z.string().min(1, "template is required")
|
|
982
|
+
});
|
|
983
|
+
var LogStreamNameSchema = z.union([
|
|
984
|
+
z.string().min(1, "logStreamName string must not be empty"),
|
|
985
|
+
LogStreamPatternConfigSchema,
|
|
986
|
+
LogStreamTemplateConfigSchema
|
|
987
|
+
]);
|
|
924
988
|
var CloudWatchConfigSchema = z.object({
|
|
925
989
|
level: LogLevelSchema.optional(),
|
|
926
990
|
rules: z.array(LevelRuleSchema).optional(),
|
|
@@ -929,7 +993,7 @@ var CloudWatchConfigSchema = z.object({
|
|
|
929
993
|
maxRetries: z.number().int().nonnegative().optional(),
|
|
930
994
|
retryDelay: z.number().int().positive().optional(),
|
|
931
995
|
logGroupName: z.string().min(1, "logGroupName is required"),
|
|
932
|
-
logStreamName:
|
|
996
|
+
logStreamName: LogStreamNameSchema.optional(),
|
|
933
997
|
region: z.string().min(1, "region is required"),
|
|
934
998
|
accessKeyId: z.string().min(1, "accessKeyId is required"),
|
|
935
999
|
secretAccessKey: z.string().min(1, "secretAccessKey is required"),
|
|
@@ -948,6 +1012,10 @@ var CallerConfigSchema = z.object({
|
|
|
948
1012
|
depth: z.number().int().nonnegative().optional(),
|
|
949
1013
|
fullPath: z.boolean().optional()
|
|
950
1014
|
});
|
|
1015
|
+
var AutoShutdownConfigSchema = z.object({
|
|
1016
|
+
timeout: z.number().int().positive().optional(),
|
|
1017
|
+
signals: z.array(z.string()).optional()
|
|
1018
|
+
});
|
|
951
1019
|
var LoggerConfigSchema = z.object({
|
|
952
1020
|
level: LevelConfigSchema,
|
|
953
1021
|
console: ConsoleConfigSchema,
|
|
@@ -956,7 +1024,8 @@ var LoggerConfigSchema = z.object({
|
|
|
956
1024
|
telegram: z.union([TelegramConfigSchema, z.array(TelegramConfigSchema)]).optional(),
|
|
957
1025
|
cloudwatch: z.union([CloudWatchConfigSchema, z.array(CloudWatchConfigSchema)]).optional(),
|
|
958
1026
|
caller: z.union([z.boolean(), CallerConfigSchema]).optional(),
|
|
959
|
-
hostname: z.string().optional()
|
|
1027
|
+
hostname: z.string().optional(),
|
|
1028
|
+
autoShutdown: z.union([z.boolean(), AutoShutdownConfigSchema]).optional()
|
|
960
1029
|
});
|
|
961
1030
|
function validateConfig(config) {
|
|
962
1031
|
return LoggerConfigSchema.parse(config);
|
|
@@ -1025,6 +1094,53 @@ function formatCallerInfo(info) {
|
|
|
1025
1094
|
return location;
|
|
1026
1095
|
}
|
|
1027
1096
|
|
|
1097
|
+
// src/utils/shutdown.ts
|
|
1098
|
+
var DEFAULT_OPTIONS2 = {
|
|
1099
|
+
timeout: 5e3,
|
|
1100
|
+
exitCode: 0,
|
|
1101
|
+
signals: ["SIGTERM", "SIGINT"]
|
|
1102
|
+
};
|
|
1103
|
+
var registered = false;
|
|
1104
|
+
function registerShutdown(logger, options = {}) {
|
|
1105
|
+
const opts = { ...DEFAULT_OPTIONS2, ...options };
|
|
1106
|
+
const shutdown = async (signal) => {
|
|
1107
|
+
if (signal) {
|
|
1108
|
+
console.error(`[Logger] Received ${signal}, shutting down gracefully...`);
|
|
1109
|
+
}
|
|
1110
|
+
try {
|
|
1111
|
+
if (opts.onShutdown) {
|
|
1112
|
+
await opts.onShutdown();
|
|
1113
|
+
}
|
|
1114
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
1115
|
+
setTimeout(() => {
|
|
1116
|
+
reject(new Error(`Shutdown timed out after ${opts.timeout}ms`));
|
|
1117
|
+
}, opts.timeout);
|
|
1118
|
+
});
|
|
1119
|
+
await Promise.race([
|
|
1120
|
+
logger.shutdown(),
|
|
1121
|
+
timeoutPromise
|
|
1122
|
+
]);
|
|
1123
|
+
if (signal) {
|
|
1124
|
+
console.error("[Logger] Graceful shutdown completed");
|
|
1125
|
+
process.exit(opts.exitCode);
|
|
1126
|
+
}
|
|
1127
|
+
} catch (error) {
|
|
1128
|
+
console.error("[Logger] Shutdown error:", error instanceof Error ? error.message : error);
|
|
1129
|
+
if (signal) {
|
|
1130
|
+
process.exit(1);
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
};
|
|
1134
|
+
if (!registered) {
|
|
1135
|
+
for (const signal of opts.signals) {
|
|
1136
|
+
process.on(signal, () => void shutdown(signal));
|
|
1137
|
+
}
|
|
1138
|
+
process.on("beforeExit", () => void shutdown());
|
|
1139
|
+
registered = true;
|
|
1140
|
+
}
|
|
1141
|
+
return shutdown;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1028
1144
|
// src/logger.ts
|
|
1029
1145
|
var Logger = class _Logger {
|
|
1030
1146
|
constructor(state, context) {
|
|
@@ -1035,7 +1151,12 @@ var Logger = class _Logger {
|
|
|
1035
1151
|
static create(config, store) {
|
|
1036
1152
|
const validatedConfig = validateConfig(config);
|
|
1037
1153
|
const state = createState(validatedConfig, store);
|
|
1038
|
-
|
|
1154
|
+
const logger = new _Logger(state, "APP");
|
|
1155
|
+
if (validatedConfig.autoShutdown) {
|
|
1156
|
+
const shutdownConfig = typeof validatedConfig.autoShutdown === "object" ? validatedConfig.autoShutdown : {};
|
|
1157
|
+
registerShutdown(logger, shutdownConfig);
|
|
1158
|
+
}
|
|
1159
|
+
return logger;
|
|
1039
1160
|
}
|
|
1040
1161
|
for(context) {
|
|
1041
1162
|
return new _Logger(this.state, context);
|
|
@@ -1077,6 +1198,15 @@ var Logger = class _Logger {
|
|
|
1077
1198
|
getLevelOverrides() {
|
|
1078
1199
|
return Array.from(this.state.levelOverrides.values());
|
|
1079
1200
|
}
|
|
1201
|
+
// Shutdown
|
|
1202
|
+
/**
|
|
1203
|
+
* Gracefully shutdown the logger, flushing all pending messages.
|
|
1204
|
+
* Should be called before process exit to ensure no logs are lost.
|
|
1205
|
+
*/
|
|
1206
|
+
async shutdown() {
|
|
1207
|
+
const closePromises = this.state.transports.map((transport) => transport.close());
|
|
1208
|
+
await Promise.all(closePromises);
|
|
1209
|
+
}
|
|
1080
1210
|
// Profiling
|
|
1081
1211
|
profile(id, meta) {
|
|
1082
1212
|
const existing = this.profileTimers.get(id);
|
|
@@ -1343,16 +1473,28 @@ function createMasker(options = {}) {
|
|
|
1343
1473
|
}
|
|
1344
1474
|
|
|
1345
1475
|
// src/formatters.ts
|
|
1346
|
-
|
|
1476
|
+
var MAX_FLATTEN_DEPTH = 10;
|
|
1477
|
+
var CIRCULAR_REF = "[Circular]";
|
|
1478
|
+
function flattenObject(obj, prefix = "", ancestors = /* @__PURE__ */ new WeakSet(), depth = 0) {
|
|
1347
1479
|
const result = {};
|
|
1480
|
+
if (depth > MAX_FLATTEN_DEPTH) {
|
|
1481
|
+
result[prefix || "value"] = "[Max depth exceeded]";
|
|
1482
|
+
return result;
|
|
1483
|
+
}
|
|
1484
|
+
ancestors.add(obj);
|
|
1348
1485
|
for (const [key, value] of Object.entries(obj)) {
|
|
1349
1486
|
const newKey = prefix ? `${prefix}.${key}` : key;
|
|
1350
1487
|
if (value !== null && typeof value === "object" && !Array.isArray(value) && !(value instanceof Error)) {
|
|
1351
|
-
|
|
1488
|
+
if (ancestors.has(value)) {
|
|
1489
|
+
result[newKey] = CIRCULAR_REF;
|
|
1490
|
+
} else {
|
|
1491
|
+
Object.assign(result, flattenObject(value, newKey, ancestors, depth + 1));
|
|
1492
|
+
}
|
|
1352
1493
|
} else {
|
|
1353
1494
|
result[newKey] = value;
|
|
1354
1495
|
}
|
|
1355
1496
|
}
|
|
1497
|
+
ancestors.delete(obj);
|
|
1356
1498
|
return result;
|
|
1357
1499
|
}
|
|
1358
1500
|
function formatLogfmtValue(value) {
|
|
@@ -1381,6 +1523,6 @@ function formatLogfmt(data) {
|
|
|
1381
1523
|
return Object.entries(flattened).filter(([, value]) => value !== void 0 && value !== null).map(([key, value]) => `${key}=${formatLogfmtValue(value)}`).join(" ");
|
|
1382
1524
|
}
|
|
1383
1525
|
|
|
1384
|
-
export { BaseHttpTransport, CallerConfigSchema, CloudWatchConfigSchema, CloudWatchTransport, ConsoleConfigSchema, DiscordConfigSchema, DiscordTransport, FileConfigSchema, HttpTransportBaseConfigSchema, LOG_LEVELS, LevelConfigObjectSchema, LevelConfigSchema, LevelRuleSchema, LogFormatSchema, LogLevelSchema, Logger, LoggerConfigSchema, LoggerStore, MessageBuffer, TelegramConfigSchema, TelegramTransport, assertLogLevel, createMasker, createSingletonLogger, extractRequestId, flattenObject, formatCallerInfo, formatLogfmt, formatLogfmtValue, generateRequestId, getCallerInfo, getOrGenerateRequestId, isValidLogLevel, maskSecrets, matchesContext, measureAsync, measureSync, safeValidateConfig, validateConfig };
|
|
1526
|
+
export { AutoShutdownConfigSchema, BaseHttpTransport, CallerConfigSchema, CloudWatchConfigSchema, CloudWatchTransport, ConsoleConfigSchema, DiscordConfigSchema, DiscordTransport, FileConfigSchema, HttpTransportBaseConfigSchema, LOG_LEVELS, LevelConfigObjectSchema, LevelConfigSchema, LevelRuleSchema, LogFormatSchema, LogLevelSchema, LogStreamNameSchema, LogStreamPatternConfigSchema, LogStreamPatternSchema, LogStreamTemplateConfigSchema, Logger, LoggerConfigSchema, LoggerStore, MessageBuffer, TelegramConfigSchema, TelegramTransport, assertLogLevel, createMasker, createSingletonLogger, extractRequestId, flattenObject, formatCallerInfo, formatLogfmt, formatLogfmtValue, generateRequestId, getCallerInfo, getOrGenerateRequestId, isValidLogLevel, maskSecrets, matchesContext, measureAsync, measureSync, registerShutdown, safeValidateConfig, validateConfig };
|
|
1385
1527
|
//# sourceMappingURL=index.mjs.map
|
|
1386
1528
|
//# sourceMappingURL=index.mjs.map
|