Reputation: 131
We are using pino-http with express for logging the REST API requests we receive in our application.
When there are errors, we log the error object which provides information about what went wrong.
However, when pino-http logs the info for the failed HTTP request, the error message is set to "failed with status code 500", which is too generic and provides no useful information. It does not log the message from the error object.
The setup is
// pino.ts
import pino, { type LoggerOptions } from 'pino';
import http from 'pino-http';
const options: LoggerOptions = {
};
options.timestamp = () => `,"time":"${new Date(Date.now()).toISOString()}"`;
options.formatters = {
level: (label) => {
return { level : label.toUpperCase() };
},
};
const logger = pino(options);
const httpLogger = http({
logger,
},
serializers: {
// Do not include extensive error message in log output
err: (err) => err.message
},
customErrorMessage: (_req, _res, err) => err.message,
});
export { httpLogger };
//express.ts
import express from 'express';
import { httpLogger } from './pino';
const app = express();
app.use(httpLogger);
...
I expect that pino-http logs the message from the error object rather than 'failed with status code 500'
Is this possible, or is it a limitation with pino-http ?
Upvotes: 1
Views: 686
Reputation: 2847
I've encountered a similar issue and fixed it by specifying custom express error handler and setting res.err
to the error object.
This is how pino-http
decides the error object to serialize to the log:
// from pino-http logger.js (inside function onResFinished)
const error = err || res.err || new Error('failed with status code ' + res.statusCode)
So what is happening is that the err
object passed to onResFinished(res, logger, err)
by an express callback is undefined
and the same goes for res.err
, resulting in the "failed with status code" message.
The solution is to either use res.emit("error", err)
as pino-http
listens to events emitted by the response, or set res.err
.
Custom express error handler:
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
// set error for pino-http to log
res.err = err;
// let express default error handler handle the error (optional)
next(err);
});
To integrate into your code:
// express.ts
import express, { Request, Response, NextFunction } from 'express';
import { httpLogger } from './pino';
const app = express();
app.use(httpLogger);
...
// middlewares and routes
app.use(someMiddleware())
app.use(someRouter())
app.get("/test", (req, res) => {
// test if error handling is working
throw new Error("this is bad...");
});
...
// put the custom error handler as last app.use() call
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
// set error for pino-http to log
res.err = err;
// let express default error handler handle the error (optional)
next(err);
});
// start the server
app.listen(...)
Sources:
Upvotes: 0
Reputation: 721
I had a similar issue when I used a custom error handler. I logged the error in my error handler and returned a JSON response with a 500 status. pino-http
created a log entry for the request and since the status was a 500 it logged an error but the Error object was generated by pino-http with the message "failed with status code 500". Setting customErrorObject
in the logger config allows you to specify the Error object that gets logged, and customErrorMessage
allows you to specify the message.
In my case since I already processed the original error in my custom error handler it made more sense for the request log to simply note the 500 status and not log the error object. In your case if you haven't specified a custom error handler, the original error is probably getting processed by the Express default error handler. Creating a custom error handler and logging the error object from there is probably your best bet.
Upvotes: 1