pino-http

repository·master·Indexed 20 days ago

https://github.com/pinojs/pino-http

A high-speed HTTP logger for Node.js designed to log request/response pairs with minimal overhead. It provides middleware for Node.js HTTP servers and Express, attaching a logger to the request object (req.log) for request-scoped logging. Features include customizable log levels via customLogLevel, request ID generation, custom serializers for req/res/err, and support for Pino transports for custom log formatting.

Tokens
3.5K
Snippets
14
Records
15
Agent score
22%

What's inside pino-http

  1. Customize structured log objects with hooks

    master

    You can augment the base loggable object for received, successful, or errored requests using object hooks. These hooks receive the current loggable object (val) and allow you to merge in custom event labels or metadata.

    const logger = require('pino-http')({
      customReceivedObject: (req, res, val) => {
        return {
          ...val,
          category: 'ApplicationEvent',
          eventCode: 'REQUEST_RECEIVED'
        };
      },
    
      customSuccessObject: (req, res, val) => {
        return {
          ...val,
          category: 'ApplicationEvent',
          eventCode: res.statusCode < 300 ? 'REQUEST_PROCESSED' : 'REQUEST_FAILED'
        };
      },
    
      customErrorObject: (req, res, error, val) => {
        return {
          ...val,
          category: 'ApplicationEvent',
          eventCode: 'REQUEST_FAILED'
        };
      }
    })
  2. Access the logger instance via req.log or res.log

    master

    The pino-http middleware attaches logger instances to the request and response objects, allowing you to perform manual logging within your route handlers.

    • req.log: The logger instance used for request-related logs. If quietReqLogger is enabled, this will be the base logger.
    • res.log: The logger instance used for response-related logs.
    • req.allLogs / res.allLogs: Arrays containing the logger instances used during the lifecycle.

    If quietReqLogger is enabled, req.log and res.log will point to the base logger instead of a child logger containing the full request object, which helps reduce log verbosity.

    const pinoHttp = require('pino-http');
    const http = require('http');
    
    const server = http.createServer((req, res) => {
      pinoHttp()(req, res, () => {
        // Use the attached logger for manual logging
        req.log.info('Processing business logic...');
        res.end('Done');
      });
    });
  3. Basic usage example of pino-http

    master

    To use pino-http, initialize it by calling require('pino-http')(). In your HTTP request handler, call the returned logger instance with the req and res objects. This attaches a logger to the request object (req.log), allowing you to log custom messages within the context of that specific request. The logger automatically handles logging the request and response details.

    'use strict'
    
    const http = require('http')
    const server = http.createServer(handle)
    
    const logger = require('pino-http')()
    
    function handle (req, res) {
      logger(req, res)
      req.log.info('something else')
      res.end('hello world')
    }
    
    server.listen(3000)
  4. Initialize pino-http as Express middleware

    master

    To use pino-http with Express, import the module and pass it to app.use(). This automatically attaches a logger to the req and res objects, allowing you to log request-scoped information.

    const express = require('express')
    const logger = require('pino-http')
    
    const app = express()
    
    app.use(logger())
    
    function handle (req, res) {
      req.log.info('something else')
      res.end('hello world')
    }
    
    app.listen(3000)
  5. Use Pino transports for custom log formatting

    master

    You can customize the final output format of your logs by passing a transport configuration object in the pinoHttp options. This allows you to use external packages like pino-http-print for human-readable output.

    const logger = require('pino-http')({
      quietReqLogger: true,
      transport: {
        target: 'pino-http-print',
        options: {
          destination: 1,
          all: true,
          translateTime: true
        }
      }
    })
  6. Configure pinoHttp options

    master

    The pinoHttp([opts], [stream]) function accepts an options object to customize logging behavior. Key options include:

    • logger: A parent pino instance used to create the child logger.
    • genReqId: A function (req, res) => string | number to generate custom request IDs.
    • useLevel: The logging level for responses (default: 'info').
    • customLogLevel: A function (req, res, err) => string to dynamically determine the log level. Mutually exclusive with useLevel.
    • autoLogging: Boolean or object to enable/disable automatic request/error logging. Defaults to true.
    • customReceivedMessage, customSuccessMessage, customErrorMessage: Functions to customize the msg property for different request stages.
    • customAttributeKeys: Object to rename default keys like req, res, err, and responseTime.
    • customProps: Function or object to add additional properties to every log entry.
    • quietReqLogger / quietResLogger: Boolean to reduce bindings on req.log and res.log to just the reqId.
    const logger = require('pino-http')({
      logger: pino(),
      genReqId: function (req, res) {
        const existingID = req.id ?? req.headers["x-request-id"]
        if (existingID) return existingID
        const id = randomUUID()
        res.setHeader('X-Request-Id', id)
        return id
      },
      useLevel: 'info',
      customAttributeKeys: {
        req: 'request',
        res: 'response',
        err: 'error',
        responseTime: 'timeTaken'
      }
    })
  7. Configure custom serializers

    master

    You can extend or replace the default serializers for req, res, and err.

    • To extend standard serializers: Provide a function in the serializers option.
    • To work with raw values: Set wrapSerializers: false. This passes the raw IncomingMessage or ServerResponse directly to your serializer instead of the pre-serialized object.

    Note on Request Bodies: Logging request bodies is disabled by default for security. To enable it, you must manually assign the body to the request object within a custom req serializer.

    // Example: Logging request body
    const logger = require('pino-http')({
      serializers: {
        req(req) {
          req.body = req.raw.body;
          return req;
        },
      },
    });
    
    // Example: Using raw values (wrapSerializers: false)
    const logger = require('pino-http')({
      wrapSerializers: false,
      serializers: {
        req (req) {
          return { message: req.foo };
        }
      }
    })
  8. Configure custom attribute keys in pino-http

    master

    By default, pino-http uses specific keys for request, response, error, request ID, and response time. You can customize these names using the customAttributeKeys option to match your existing logging schema.

    Supported keys:

    • req: The request object key (default: 'req')
    • res: The response object key (default: 'res')
    • err: The error object key (default: 'err')
    • reqId: The request ID key (default: 'reqId')
    • responseTime: The response time key (default: 'responseTime')

    Note: These keys are used both in the log object and for the serializers.

    const pinoHttp = require('pino-http');
    
    const middleware = pinoHttp({
      customAttributeKeys: {
        req: 'request',
        res: 'response',
        err: 'error',
        reqId: 'id',
        responseTime: 'duration'
      }
    });
  9. Exclude specific requests from auto-logging

    master

    To prevent certain requests (like health checks or heartbeat endpoints) from generating completion logs, use the autoLogging.ignore option. This option accepts a function that receives (req, res) and returns true if the request should be ignored.

    const pinoHttp = require('pino-http');
    
    const middleware = pinoHttp({
      autoLogging: {
        ignore: (req, res) => req.url === '/healthz'
      }
    });
  10. Initialize the pino-http middleware

    master

    The pinoLogger function (exported as pinoHttp) is the main entrypoint. It returns a middleware function compatible with Node.js HTTP servers. You can pass an options object and an optional stream to configure the logger.

    Key configuration options include:

    • customAttributeKeys: Object to rename default keys for req, res, err, reqId, and responseTime.
    • customLogLevel: A function (req, res, err) => string to dynamically determine the log level.
    • useLevel: The default log level (e.g., 'info') used if customLogLevel doesn't return a valid level.
    • autoLogging: Boolean (defaults to true). If false, the middleware won't automatically log request completion.
    • autoLogging.ignore: A function (req, res) => boolean to skip auto-logging for specific requests.
    • genReqId: A function (req, res) => string to generate custom request IDs.
    • customProps: A function (req, res) => object or an object to add custom properties to the logger child instance.
    const pinoHttp = require('pino-http');
    const http = require('http');
    
    const server = http.createServer((req, res) => {
      // Use the middleware
      pinoHttp({ level: 'info' })(req, res, () => {
        res.end('Hello World');
      });
    });
    
    server.listen(3000);
  11. Customize log levels dynamically with customLogLevel

    master

    You can control the log level of the completion log (success or error) by providing a customLogLevel function. This function receives (req, res, err) and should return a valid log level string. If you provide this, you cannot use the useLevel option.

    Example: Logging only errors as error and everything else as debug.

    const pinoHttp = require('pino-http');
    
    const middleware = pinoHttp({
      customLogLevel: (req, res, err) => {
        if (err || res.statusCode >= 500) return 'error';
        return 'debug';
      }
    });