Pino HTTP Logger Middleware
pino-http is a high-speed HTTP logger middleware designed for Node.js applications, leveraging the highly performant Pino logging library. It focuses on providing structured JSON logs for HTTP requests and responses with minimal overhead, making it ideal for high-throughput microservices and APIs. The package is currently at version 11.0.0 and follows a release cadence that often aligns with major updates to its underlying `pino` dependency, typically seeing several minor/patch releases throughout the year. Its primary differentiator is its exceptional performance compared to other HTTP loggers, achieved by deferring heavy log processing. It provides extensive customization options for log levels, request ID generation, and structured log data.
Common errors
-
logs are not formatted/colored, they are raw JSON
cause `pino-pretty` is not installed or not used in the transport configuration.fixInstall `pino-pretty` (`npm i -D pino-pretty`) and configure Pino's `transport` option, typically only for development environments, to use it. -
Error: Cannot find module 'pino-http' or type errors related to `pinoHttp` not being a function
cause Incorrect CommonJS `require` usage or improper ESM import for the factory function.fixFor CommonJS, ensure `const pinoHttp = require('pino-http')();` (with `()`). For ESM, use `import pinoHttp from 'pino-http';` then call it as `pinoHttp();`. -
TypeError: Cannot read properties of undefined (reading 'info') on req.log
cause `pino-http` middleware function was not called or executed for the given request/response cycle, or was called incorrectly.fixEnsure `loggerMiddleware(req, res);` (where `loggerMiddleware` is your `pinoHttp()` instance) is called early in your HTTP request handling chain, typically as the first middleware in frameworks like Express. -
Argument of type 'string' is not assignable to parameter of type 'PinoLogLevel'.
cause Using a custom log level string that is not recognized by Pino's default types or declared in your custom levels.fixEnsure that custom log levels are correctly defined in your Pino configuration via the `customLevels` option and that the `useLevel` or `customLogLevel` functions return one of these defined levels or standard Pino levels.
Warnings
- breaking pino-http v11.x, which updates to pino v10.x, officially drops support for Node.js 18. Users on Node.js 18 or older should use pino-http v10.x or upgrade their Node.js version.
- gotcha The `customLogLevel` and `useLevel` options are mutually exclusive. Providing both will result in undefined behavior or one overriding the other in an unexpected way.
- gotcha By default, `pino-http` automatically logs a 'request completed' or 'request errored' message. To disable this, set `autoLogging: false` in the options. To selectively ignore certain requests from being logged, use `autoLogging.ignore`.
- gotcha Logging sensitive request bodies or adding extensive custom serializers can negatively impact performance and introduce security risks by exposing private data.
- gotcha The default `genReqId` (request ID generator) uses an integer counter which might not be unique across multiple application instances or restarts. For robust production environments, a more unique ID generation strategy is recommended.
Install
-
npm install pino-http -
yarn add pino-http -
pnpm add pino-http
Imports
- pinoHttp
const pinoHttp = require('pino-http'); // Requires a function call to initialize: const pinoHttp = require('pino-http')();import pinoHttp from 'pino-http'; // or import createPinoHttp from 'pino-http';
- PinoHttpLogger
import type { PinoHttpLogger } from 'pino-http'; - PinoLogger
import type { Logger as PinoLogger } from 'pino';
Quickstart
import http from 'node:http';
import pinoHttp from 'pino-http';
import pino from 'pino';
// Create a base Pino logger (optional, pino-http can create one internally)
const baseLogger = pino({
level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',
transport: {
target: 'pino-pretty',
options: {
colorize: true
}
}
});
const loggerMiddleware = pinoHttp({
logger: baseLogger,
genReqId: function (req) {
// Generate a unique request ID for better traceability
return req.headers['x-request-id'] || Math.random().toString(36).substring(2, 15);
},
customLogLevel: function (req, res, err) {
if (res.statusCode >= 400 && res.statusCode < 500) return 'warn';
if (res.statusCode >= 500 || err) return 'error';
return 'info';
}
});
const server = http.createServer((req, res) => {
loggerMiddleware(req, res);
req.log.info({ url: req.url, method: req.method }, 'Incoming request');
if (req.url === '/error') {
req.log.error('Simulating an error');
res.statusCode = 500;
res.end('Internal Server Error');
return;
}
res.end('Hello world!');
});
const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
baseLogger.info(`Server listening on port ${PORT}`);
});
// To run: node --loader ts-node/esm your-file.ts (if using ts-node and ESM)
// or: node your-file.js