@rawnodes/logger 2.5.0 → 2.7.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 +62 -0
- package/dist/index.d.mts +98 -6
- package/dist/index.d.ts +98 -6
- package/dist/index.js +233 -13
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +231 -14
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -1
package/dist/index.mjs
CHANGED
|
@@ -683,6 +683,7 @@ function createFormattedFilterStream(format, level, rules, store, destination) {
|
|
|
683
683
|
}
|
|
684
684
|
function createStreams(config, store) {
|
|
685
685
|
const streams = [];
|
|
686
|
+
const transports = [];
|
|
686
687
|
const consoleStream = createFormattedFilterStream(
|
|
687
688
|
config.console.format,
|
|
688
689
|
config.console.level,
|
|
@@ -730,6 +731,7 @@ function createStreams(config, store) {
|
|
|
730
731
|
}
|
|
731
732
|
for (const discordConfig of toArray(config.discord)) {
|
|
732
733
|
const transport = new DiscordTransport(discordConfig);
|
|
734
|
+
transports.push(transport);
|
|
733
735
|
const discordStream = createHttpTransportStream(transport, discordConfig.level, discordConfig.rules, store);
|
|
734
736
|
streams.push({
|
|
735
737
|
level: "trace",
|
|
@@ -738,6 +740,7 @@ function createStreams(config, store) {
|
|
|
738
740
|
}
|
|
739
741
|
for (const telegramConfig of toArray(config.telegram)) {
|
|
740
742
|
const transport = new TelegramTransport(telegramConfig);
|
|
743
|
+
transports.push(transport);
|
|
741
744
|
const telegramStream = createHttpTransportStream(transport, telegramConfig.level, telegramConfig.rules, store);
|
|
742
745
|
streams.push({
|
|
743
746
|
level: "trace",
|
|
@@ -746,13 +749,17 @@ function createStreams(config, store) {
|
|
|
746
749
|
}
|
|
747
750
|
for (const cloudwatchConfig of toArray(config.cloudwatch)) {
|
|
748
751
|
const transport = new CloudWatchTransport(cloudwatchConfig, config.hostname);
|
|
752
|
+
transports.push(transport);
|
|
749
753
|
const cwStream = createHttpTransportStream(transport, cloudwatchConfig.level, cloudwatchConfig.rules, store);
|
|
750
754
|
streams.push({
|
|
751
755
|
level: "trace",
|
|
752
756
|
stream: cwStream
|
|
753
757
|
});
|
|
754
758
|
}
|
|
755
|
-
return
|
|
759
|
+
return {
|
|
760
|
+
destination: pino.multistream(streams),
|
|
761
|
+
transports
|
|
762
|
+
};
|
|
756
763
|
}
|
|
757
764
|
function toArray(value) {
|
|
758
765
|
if (!value) return [];
|
|
@@ -837,14 +844,14 @@ function createState(config, store) {
|
|
|
837
844
|
levelOverrides.set(key, rule);
|
|
838
845
|
}
|
|
839
846
|
const { contextIndex, complexRules } = buildIndexes(levelOverrides);
|
|
840
|
-
const
|
|
847
|
+
const { destination, transports } = createStreams(config, loggerStore);
|
|
841
848
|
const options = {
|
|
842
849
|
level: "trace",
|
|
843
850
|
// Accept all, we filter in shouldLog()
|
|
844
851
|
customLevels: CUSTOM_LEVELS,
|
|
845
852
|
base: { hostname: config.hostname ?? hostname() }
|
|
846
853
|
};
|
|
847
|
-
const pinoLogger = pino(options,
|
|
854
|
+
const pinoLogger = pino(options, destination);
|
|
848
855
|
let callerConfig;
|
|
849
856
|
if (config.caller === true) {
|
|
850
857
|
callerConfig = {};
|
|
@@ -858,7 +865,8 @@ function createState(config, store) {
|
|
|
858
865
|
levelOverrides,
|
|
859
866
|
contextIndex,
|
|
860
867
|
complexRules,
|
|
861
|
-
callerConfig
|
|
868
|
+
callerConfig,
|
|
869
|
+
transports
|
|
862
870
|
};
|
|
863
871
|
}
|
|
864
872
|
function rebuildIndexes(state) {
|
|
@@ -1004,6 +1012,10 @@ var CallerConfigSchema = z.object({
|
|
|
1004
1012
|
depth: z.number().int().nonnegative().optional(),
|
|
1005
1013
|
fullPath: z.boolean().optional()
|
|
1006
1014
|
});
|
|
1015
|
+
var AutoShutdownConfigSchema = z.object({
|
|
1016
|
+
timeout: z.number().int().positive().optional(),
|
|
1017
|
+
signals: z.array(z.string()).optional()
|
|
1018
|
+
});
|
|
1007
1019
|
var LoggerConfigSchema = z.object({
|
|
1008
1020
|
level: LevelConfigSchema,
|
|
1009
1021
|
console: ConsoleConfigSchema,
|
|
@@ -1012,7 +1024,8 @@ var LoggerConfigSchema = z.object({
|
|
|
1012
1024
|
telegram: z.union([TelegramConfigSchema, z.array(TelegramConfigSchema)]).optional(),
|
|
1013
1025
|
cloudwatch: z.union([CloudWatchConfigSchema, z.array(CloudWatchConfigSchema)]).optional(),
|
|
1014
1026
|
caller: z.union([z.boolean(), CallerConfigSchema]).optional(),
|
|
1015
|
-
hostname: z.string().optional()
|
|
1027
|
+
hostname: z.string().optional(),
|
|
1028
|
+
autoShutdown: z.union([z.boolean(), AutoShutdownConfigSchema]).optional()
|
|
1016
1029
|
});
|
|
1017
1030
|
function validateConfig(config) {
|
|
1018
1031
|
return LoggerConfigSchema.parse(config);
|
|
@@ -1081,6 +1094,176 @@ function formatCallerInfo(info) {
|
|
|
1081
1094
|
return location;
|
|
1082
1095
|
}
|
|
1083
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
|
+
|
|
1144
|
+
// src/utils/error.ts
|
|
1145
|
+
function isAxiosError(error) {
|
|
1146
|
+
return error !== null && typeof error === "object" && "isAxiosError" in error && error.isAxiosError === true;
|
|
1147
|
+
}
|
|
1148
|
+
function sanitizeResponseData(data, maxStringLength = 1e4) {
|
|
1149
|
+
if (data === null || data === void 0) {
|
|
1150
|
+
return data;
|
|
1151
|
+
}
|
|
1152
|
+
if (typeof data === "string") {
|
|
1153
|
+
return data.length > maxStringLength ? `${data.slice(0, maxStringLength)}...[truncated]` : data;
|
|
1154
|
+
}
|
|
1155
|
+
if (typeof data === "number" || typeof data === "boolean") {
|
|
1156
|
+
return data;
|
|
1157
|
+
}
|
|
1158
|
+
if (Array.isArray(data)) {
|
|
1159
|
+
return data.slice(0, 100).map((item) => sanitizeResponseData(item, maxStringLength));
|
|
1160
|
+
}
|
|
1161
|
+
if (typeof data === "object") {
|
|
1162
|
+
const result = {};
|
|
1163
|
+
const entries = Object.entries(data);
|
|
1164
|
+
for (const [key, value] of entries.slice(0, 50)) {
|
|
1165
|
+
result[key] = sanitizeResponseData(value, maxStringLength);
|
|
1166
|
+
}
|
|
1167
|
+
return result;
|
|
1168
|
+
}
|
|
1169
|
+
return String(data);
|
|
1170
|
+
}
|
|
1171
|
+
function extractAxiosHttpData(error) {
|
|
1172
|
+
const httpData = {};
|
|
1173
|
+
if (error.code) {
|
|
1174
|
+
httpData.code = error.code;
|
|
1175
|
+
}
|
|
1176
|
+
if (error.response) {
|
|
1177
|
+
httpData.status = error.response.status;
|
|
1178
|
+
httpData.statusText = error.response.statusText;
|
|
1179
|
+
if (error.response.data !== void 0) {
|
|
1180
|
+
httpData.responseData = sanitizeResponseData(error.response.data);
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
if (error.config) {
|
|
1184
|
+
const { url, baseURL, method } = error.config;
|
|
1185
|
+
if (url) {
|
|
1186
|
+
httpData.url = baseURL && !url.startsWith("http") ? `${baseURL}${url}` : url;
|
|
1187
|
+
}
|
|
1188
|
+
if (method) {
|
|
1189
|
+
httpData.method = method.toUpperCase();
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
return httpData;
|
|
1193
|
+
}
|
|
1194
|
+
function extractGenericHttpData(error) {
|
|
1195
|
+
const httpData = {};
|
|
1196
|
+
let hasData = false;
|
|
1197
|
+
if (typeof error.code === "string") {
|
|
1198
|
+
httpData.code = error.code;
|
|
1199
|
+
hasData = true;
|
|
1200
|
+
}
|
|
1201
|
+
const response = error.response;
|
|
1202
|
+
if (response && typeof response === "object") {
|
|
1203
|
+
if (typeof response.status === "number") {
|
|
1204
|
+
httpData.status = response.status;
|
|
1205
|
+
hasData = true;
|
|
1206
|
+
}
|
|
1207
|
+
if (typeof response.statusText === "string") {
|
|
1208
|
+
httpData.statusText = response.statusText;
|
|
1209
|
+
hasData = true;
|
|
1210
|
+
}
|
|
1211
|
+
if (response.data !== void 0) {
|
|
1212
|
+
httpData.responseData = sanitizeResponseData(response.data);
|
|
1213
|
+
hasData = true;
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
const config = error.config;
|
|
1217
|
+
if (config && typeof config === "object") {
|
|
1218
|
+
const url = config.url;
|
|
1219
|
+
const baseURL = config.baseURL;
|
|
1220
|
+
if (typeof url === "string") {
|
|
1221
|
+
httpData.url = baseURL && !url.startsWith("http") ? `${baseURL}${url}` : url;
|
|
1222
|
+
hasData = true;
|
|
1223
|
+
}
|
|
1224
|
+
if (typeof config.method === "string") {
|
|
1225
|
+
httpData.method = config.method.toUpperCase();
|
|
1226
|
+
hasData = true;
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
return hasData ? httpData : void 0;
|
|
1230
|
+
}
|
|
1231
|
+
function serializeError(error) {
|
|
1232
|
+
if (!(error instanceof Error)) {
|
|
1233
|
+
if (error === null || error === void 0) {
|
|
1234
|
+
return { errorMessage: "Unknown error" };
|
|
1235
|
+
}
|
|
1236
|
+
if (typeof error === "string") {
|
|
1237
|
+
return { errorMessage: error };
|
|
1238
|
+
}
|
|
1239
|
+
const httpData2 = typeof error === "object" ? extractGenericHttpData(error) : void 0;
|
|
1240
|
+
return {
|
|
1241
|
+
errorMessage: String(error),
|
|
1242
|
+
http: httpData2
|
|
1243
|
+
};
|
|
1244
|
+
}
|
|
1245
|
+
const serialized = {
|
|
1246
|
+
errorMessage: error.message,
|
|
1247
|
+
stack: error.stack
|
|
1248
|
+
};
|
|
1249
|
+
if (error.name && error.name !== "Error") {
|
|
1250
|
+
serialized.errorName = error.name;
|
|
1251
|
+
}
|
|
1252
|
+
if (isAxiosError(error)) {
|
|
1253
|
+
serialized.http = extractAxiosHttpData(error);
|
|
1254
|
+
return serialized;
|
|
1255
|
+
}
|
|
1256
|
+
const errWithCode = error;
|
|
1257
|
+
if (typeof errWithCode.code === "string") {
|
|
1258
|
+
serialized.code = errWithCode.code;
|
|
1259
|
+
}
|
|
1260
|
+
const httpData = extractGenericHttpData(error);
|
|
1261
|
+
if (httpData) {
|
|
1262
|
+
serialized.http = httpData;
|
|
1263
|
+
}
|
|
1264
|
+
return serialized;
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1084
1267
|
// src/logger.ts
|
|
1085
1268
|
var Logger = class _Logger {
|
|
1086
1269
|
constructor(state, context) {
|
|
@@ -1091,7 +1274,12 @@ var Logger = class _Logger {
|
|
|
1091
1274
|
static create(config, store) {
|
|
1092
1275
|
const validatedConfig = validateConfig(config);
|
|
1093
1276
|
const state = createState(validatedConfig, store);
|
|
1094
|
-
|
|
1277
|
+
const logger = new _Logger(state, "APP");
|
|
1278
|
+
if (validatedConfig.autoShutdown) {
|
|
1279
|
+
const shutdownConfig = typeof validatedConfig.autoShutdown === "object" ? validatedConfig.autoShutdown : {};
|
|
1280
|
+
registerShutdown(logger, shutdownConfig);
|
|
1281
|
+
}
|
|
1282
|
+
return logger;
|
|
1095
1283
|
}
|
|
1096
1284
|
for(context) {
|
|
1097
1285
|
return new _Logger(this.state, context);
|
|
@@ -1133,6 +1321,15 @@ var Logger = class _Logger {
|
|
|
1133
1321
|
getLevelOverrides() {
|
|
1134
1322
|
return Array.from(this.state.levelOverrides.values());
|
|
1135
1323
|
}
|
|
1324
|
+
// Shutdown
|
|
1325
|
+
/**
|
|
1326
|
+
* Gracefully shutdown the logger, flushing all pending messages.
|
|
1327
|
+
* Should be called before process exit to ensure no logs are lost.
|
|
1328
|
+
*/
|
|
1329
|
+
async shutdown() {
|
|
1330
|
+
const closePromises = this.state.transports.map((transport) => transport.close());
|
|
1331
|
+
await Promise.all(closePromises);
|
|
1332
|
+
}
|
|
1136
1333
|
// Profiling
|
|
1137
1334
|
profile(id, meta) {
|
|
1138
1335
|
const existing = this.profileTimers.get(id);
|
|
@@ -1195,11 +1392,19 @@ var Logger = class _Logger {
|
|
|
1195
1392
|
logMeta.caller = formatCallerInfo(callerInfo);
|
|
1196
1393
|
}
|
|
1197
1394
|
}
|
|
1198
|
-
if (error
|
|
1199
|
-
|
|
1200
|
-
logMeta.
|
|
1201
|
-
|
|
1202
|
-
|
|
1395
|
+
if (error !== void 0) {
|
|
1396
|
+
const serialized = serializeError(error);
|
|
1397
|
+
logMeta.errorMessage = serialized.errorMessage;
|
|
1398
|
+
logMeta.stack = serialized.stack;
|
|
1399
|
+
if (serialized.errorName) {
|
|
1400
|
+
logMeta.errorName = serialized.errorName;
|
|
1401
|
+
}
|
|
1402
|
+
if (serialized.code) {
|
|
1403
|
+
logMeta.errorCode = serialized.code;
|
|
1404
|
+
}
|
|
1405
|
+
if (serialized.http) {
|
|
1406
|
+
logMeta.http = serialized.http;
|
|
1407
|
+
}
|
|
1203
1408
|
}
|
|
1204
1409
|
const pinoMethod = this.getPinoMethod(level);
|
|
1205
1410
|
pinoMethod.call(this.state.pino, logMeta, message);
|
|
@@ -1399,16 +1604,28 @@ function createMasker(options = {}) {
|
|
|
1399
1604
|
}
|
|
1400
1605
|
|
|
1401
1606
|
// src/formatters.ts
|
|
1402
|
-
|
|
1607
|
+
var MAX_FLATTEN_DEPTH = 10;
|
|
1608
|
+
var CIRCULAR_REF = "[Circular]";
|
|
1609
|
+
function flattenObject(obj, prefix = "", ancestors = /* @__PURE__ */ new WeakSet(), depth = 0) {
|
|
1403
1610
|
const result = {};
|
|
1611
|
+
if (depth > MAX_FLATTEN_DEPTH) {
|
|
1612
|
+
result[prefix || "value"] = "[Max depth exceeded]";
|
|
1613
|
+
return result;
|
|
1614
|
+
}
|
|
1615
|
+
ancestors.add(obj);
|
|
1404
1616
|
for (const [key, value] of Object.entries(obj)) {
|
|
1405
1617
|
const newKey = prefix ? `${prefix}.${key}` : key;
|
|
1406
1618
|
if (value !== null && typeof value === "object" && !Array.isArray(value) && !(value instanceof Error)) {
|
|
1407
|
-
|
|
1619
|
+
if (ancestors.has(value)) {
|
|
1620
|
+
result[newKey] = CIRCULAR_REF;
|
|
1621
|
+
} else {
|
|
1622
|
+
Object.assign(result, flattenObject(value, newKey, ancestors, depth + 1));
|
|
1623
|
+
}
|
|
1408
1624
|
} else {
|
|
1409
1625
|
result[newKey] = value;
|
|
1410
1626
|
}
|
|
1411
1627
|
}
|
|
1628
|
+
ancestors.delete(obj);
|
|
1412
1629
|
return result;
|
|
1413
1630
|
}
|
|
1414
1631
|
function formatLogfmtValue(value) {
|
|
@@ -1437,6 +1654,6 @@ function formatLogfmt(data) {
|
|
|
1437
1654
|
return Object.entries(flattened).filter(([, value]) => value !== void 0 && value !== null).map(([key, value]) => `${key}=${formatLogfmtValue(value)}`).join(" ");
|
|
1438
1655
|
}
|
|
1439
1656
|
|
|
1440
|
-
export { 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, safeValidateConfig, validateConfig };
|
|
1657
|
+
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, serializeError, validateConfig };
|
|
1441
1658
|
//# sourceMappingURL=index.mjs.map
|
|
1442
1659
|
//# sourceMappingURL=index.mjs.map
|