[Bug]: Errors don't get processed correctly by `format.json` / `format.simple`
Opened this issue · 0 comments
The problem
I want to log errors.
const logId = randomUUID()
export const logger = createLogger({
defaultMeta: { logId },
format: format.json(),
level: "debug",
transports: [new transports.Console({ format: format.simple() })],
})
const err = new Error("This is an error.")
logger.info("logger.info:", { error: err })
This gives me {}
.
info: logger.info: {"error":{},"logId":"d9ae9439-4a98-4b4e-ad1a-f94c617cfa30"}
I figured I would transform the error into something loggable:
/** Turns an error into a plain object. */
function parseError(err: Error): ParsedError {
return JSON.parse(
JSON.stringify(err, Object.getOwnPropertyNames(err)),
) as ParsedError
}
/** Handles proper stringification of Buffer and bigint output. */
function replacer(_key: string, value: unknown) {
if (typeof value === "bigint") return value.toString()
return value
}
const logId = randomUUID()
export const logger = createLogger({
defaultMeta: { logId },
format: format.json({
replacer: (key, value: unknown) => {
if (value instanceof Error) return parseError(value)
return replacer(key, value)
},
}),
level: "debug",
transports: [new transports.Console({ format: format.simple() })],
})
// Tests
const err = new Error("This is an error.")
logger.info("logger.info:", { error: err })
console.log("console.log:", { error: parseError(err) })
However, the new output is completely detached from the code I've written.
info: logger.info: {"error":{"originalColumn":22,"originalLine":24},"logId":"abe10e0e-0b04-4651-be81-00ede4fca436"}
console.log: {
error: {
message: "This is an error.",
originalLine: 24,
originalColumn: 22,
line: 24,
column: 22,
sourceURL: "/home/nato/Code/localhost/GitLabAPI/src/logger.ts",
stack: "Error: This is an error.\n at module code (/home/nato/Code/localhost/GitLabAPI/src/logger.ts:41:12)"
}
}
More than half of the fields were completely ignored. If I log inside the format.json
lambda, the keys are passed correctly.
What version of Logform presents the issue?
2.6.0
What version of Node are you using?
21.1.0
If this is a TypeScript issue, what version of TypeScript are you using?
5.2.2
If this worked in a previous version of Logform, which was it?
No response
Minimum Working Example
import { randomUUID } from "crypto"
import { createLogger, format, transports } from "winston"
interface ParsedError {
readonly message: string
readonly originalLine: number
readonly originalColumn: number
readonly line: number
readonly column: number
readonly sourceURL: string
readonly stack: string
}
/** Turns an error into a plain object. */
function parseError(err: Error): ParsedError {
return JSON.parse(
JSON.stringify(err, Object.getOwnPropertyNames(err)),
) as ParsedError
}
/** Handles proper stringification of Buffer and bigint output. */
function replacer(_key: string, value: unknown) {
if (typeof value === "bigint") return value.toString()
return value
}
const logId = randomUUID()
export const logger = createLogger({
defaultMeta: { logId },
format: format.json({
replacer: (key, value: unknown) => {
if (value instanceof Error) return parseError(value)
return replacer(key, value)
},
}),
level: "debug",
transports: [new transports.Console({ format: format.simple() })],
})
// Tests
const err = new Error("This is an error.")
logger.info("logger.info:", { error: err })
console.log("console.log:", { error: parseError(err) })
Additional information
If we pre-parse each error individually before passing them to Winston, then it works. The bug only appears when we send an Error
instance, which seems like the whole point of using a logger in the first place, so I'm a bit confused at why this doesn't work natively nor after hacking into it.
Btw, I don't see a reason to not export the defaut replacer
Lines 7 to 18 in ded082a
🔎 Search Terms
log error, log errors, format.json, format error, format errors