fix: preserve structured logger metadata
This commit is contained in:
+35
-16
@@ -51,16 +51,33 @@ const serializeError = (error: Error): SerializedError => {
|
|||||||
return serialized;
|
return serialized;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isPlainObject = (value: unknown): value is Record<string, unknown> => {
|
||||||
|
if (!value || typeof value !== "object") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const prototype = Object.getPrototypeOf(value);
|
||||||
|
return prototype === Object.prototype || prototype === null;
|
||||||
|
};
|
||||||
|
|
||||||
const serializeLogValue = (value: unknown): unknown => {
|
const serializeLogValue = (value: unknown): unknown => {
|
||||||
if (value instanceof Error) {
|
if (value instanceof Error) {
|
||||||
return serializeError(value);
|
return serializeError(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (value instanceof Date) {
|
||||||
|
return value.toISOString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value instanceof RegExp) {
|
||||||
|
return value.toString();
|
||||||
|
}
|
||||||
|
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
return value.map(serializeLogValue);
|
return value.map(serializeLogValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value && typeof value === "object") {
|
if (isPlainObject(value)) {
|
||||||
return Object.fromEntries(
|
return Object.fromEntries(
|
||||||
Object.entries(value).map(([key, nestedValue]) => [
|
Object.entries(value).map(([key, nestedValue]) => [
|
||||||
key,
|
key,
|
||||||
@@ -79,13 +96,15 @@ const formatLogMetadata = (metadata: LogMetadata): LogMetadata => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const metadataFormat = winston.format((info) => {
|
const metadataFormat = winston.format((info) => {
|
||||||
const { level, message, timestamp, ...metadata } = info;
|
const { level: _level, message: _message, timestamp: _timestamp, ...metadata } =
|
||||||
return {
|
info;
|
||||||
level,
|
|
||||||
message,
|
for (const key of Object.keys(metadata)) {
|
||||||
timestamp,
|
delete info[key];
|
||||||
...formatLogMetadata(metadata),
|
}
|
||||||
};
|
|
||||||
|
Object.assign(info, formatLogMetadata(metadata));
|
||||||
|
return info;
|
||||||
});
|
});
|
||||||
|
|
||||||
const consoleFormat = winston.format.printf((info) => {
|
const consoleFormat = winston.format.printf((info) => {
|
||||||
@@ -142,15 +161,15 @@ const winstonLogger = winston.createLogger({
|
|||||||
function wrapLogger(wLogger: winston.Logger): CustomLogger {
|
function wrapLogger(wLogger: winston.Logger): CustomLogger {
|
||||||
const logAtLevel = (level: string) => {
|
const logAtLevel = (level: string) => {
|
||||||
return (arg1: any, arg2?: any) => {
|
return (arg1: any, arg2?: any) => {
|
||||||
if (typeof arg1 === "object" && arg1 !== null) {
|
if (arg1 instanceof Error) {
|
||||||
// arg1 is object, arg2 is message string
|
wLogger.log(level, arg1.message, { error: arg1 });
|
||||||
const msg = typeof arg2 === "string" ? arg2 : "";
|
} else if (typeof arg1 === "object" && arg1 !== null) {
|
||||||
wLogger.log(level, msg, { ...arg1 });
|
const message = typeof arg2 === "string" ? arg2 : "";
|
||||||
|
wLogger.log(level, message, { ...arg1 });
|
||||||
} else {
|
} else {
|
||||||
// arg1 is message string, arg2 is metadata (or nothing)
|
const message = typeof arg1 === "string" ? arg1 : String(arg1);
|
||||||
const msg = typeof arg1 === "string" ? arg1 : String(arg1);
|
const metadata = typeof arg2 === "object" && arg2 !== null ? arg2 : {};
|
||||||
const meta = typeof arg2 === "object" && arg2 !== null ? arg2 : {};
|
wLogger.log(level, message, metadata);
|
||||||
wLogger.log(level, msg, meta);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
+1
-1
@@ -32,7 +32,7 @@ export async function retryWithBackoff<T>(
|
|||||||
{
|
{
|
||||||
attempt: error.attemptNumber,
|
attempt: error.attemptNumber,
|
||||||
retriesLeft: error.retriesLeft,
|
retriesLeft: error.retriesLeft,
|
||||||
error: error.error.message,
|
error: error.error,
|
||||||
},
|
},
|
||||||
"Retry attempt",
|
"Retry attempt",
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -54,4 +54,13 @@ describe("logger serialization", () => {
|
|||||||
formatLogMetadataForTest({ context: "bot", signal: "SIGINT", count: 2 }),
|
formatLogMetadataForTest({ context: "bot", signal: "SIGINT", count: 2 }),
|
||||||
).toEqual({ context: "bot", signal: "SIGINT", count: 2 });
|
).toEqual({ context: "bot", signal: "SIGINT", count: 2 });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("serializes dates and regexps without dropping values", () => {
|
||||||
|
const createdAt = new Date("2026-05-19T00:00:00.000Z");
|
||||||
|
|
||||||
|
expect(formatLogMetadataForTest({ createdAt, pattern: /voice/i })).toEqual({
|
||||||
|
createdAt: "2026-05-19T00:00:00.000Z",
|
||||||
|
pattern: "/voice/i",
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
Reference in New Issue
Block a user