Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: Errors don't get processed correctly by format.json / format.simple #290

Open
NatoBoram opened this issue Nov 9, 2023 · 0 comments

Comments

@NatoBoram
Copy link

NatoBoram commented Nov 9, 2023

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

logform/json.js

Lines 7 to 18 in ded082a

/*
* function replacer (key, value)
* Handles proper stringification of Buffer and bigint output.
*/
function replacer(key, value) {
// safe-stable-stringify does support BigInt, however, it doesn't wrap the value in quotes.
// Leading to a loss in fidelity if the resulting string is parsed.
// It would also be a breaking change for logform.
if (typeof value === 'bigint')
return value.toString();
return value;
}

🔎 Search Terms

log error, log errors, format.json, format error, format errors

@NatoBoram NatoBoram changed the title [Bug]: Errors don't get processed correctly by format.json [Bug]: Errors don't get processed correctly by format.json / format.simple Nov 9, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant