{"id":13456496,"url":"https://github.com/pinojs/pino-http","last_synced_at":"2025-05-13T19:03:05.173Z","repository":{"id":37431251,"uuid":"56715772","full_name":"pinojs/pino-http","owner":"pinojs","description":"🌲 high-speed HTTP logger for Node.js","archived":false,"fork":false,"pushed_at":"2025-04-14T15:29:55.000Z","size":308,"stargazers_count":613,"open_issues_count":39,"forks_count":116,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-05-05T21:07:51.208Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pinojs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2016-04-20T19:37:01.000Z","updated_at":"2025-05-03T10:21:08.000Z","dependencies_parsed_at":"2023-12-15T18:28:09.010Z","dependency_job_id":"69618ab2-68dc-425d-b22a-51d86c1c3950","html_url":"https://github.com/pinojs/pino-http","commit_stats":{"total_commits":272,"total_committers":68,"mean_commits":4.0,"dds":0.7352941176470589,"last_synced_commit":"15b69ae6a3fb7b88bd34268bd0fe4b62141135d9"},"previous_names":[],"tags_count":65,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pinojs%2Fpino-http","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pinojs%2Fpino-http/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pinojs%2Fpino-http/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pinojs%2Fpino-http/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pinojs","download_url":"https://codeload.github.com/pinojs/pino-http/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253187143,"owners_count":21868070,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T08:01:23.067Z","updated_at":"2025-05-13T19:03:05.129Z","avatar_url":"https://github.com/pinojs.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# pino-http\u0026nbsp;\u0026nbsp;[![Build Status](https://img.shields.io/github/actions/workflow/status/pinojs/pino-http/ci.yml?branch=master)](https://github.com/pinojs/pino-http/actions)\n\nHigh-speed HTTP logger for Node.js\n\nTo our knowledge, `pino-http` is the [fastest](#benchmarks) HTTP logger in town.\n\n* [Installation](#install)\n* [Example](#example)\n* [Benchmarks](#benchmarks)\n* [API](#api)\n* [Team](#team)\n* [Acknowledgements](#acknowledgements)\n* [License](#license)\n\n## Benchmarks\n\nBenchmarks log each request/response pair while returning\n`'hello world'`, using\n[autocannon](https://github.com/mcollina/autocannon) with 100\nconnections and 10 pipelined requests.\n\n* `http-ndjson` (equivalent info): 7730.73 req/sec\n* `http-ndjson` (standard minimum info): 9522.37 req/sec\n* `pino-http`: 21496 req/sec\n* `pino-http` (extreme): 25770.91 req/sec\n* no logger: 46139.64 req/sec\n\nAll benchmarks where taken on a Macbook Pro 2013 (2.6GHZ i7, 16GB of RAM).\n\n## Install\n\n```\nnpm i pino-http --save\n```\n\n## Example\n\n```js\n'use strict'\n\nconst http = require('http')\nconst server = http.createServer(handle)\n\nconst logger = require('pino-http')()\n\nfunction handle (req, res) {\n  logger(req, res)\n  req.log.info('something else')\n  res.end('hello world')\n}\n\nserver.listen(3000)\n```\n\n```\n$ node example.js | pino-pretty\n[2016-03-31T16:53:21.079Z] INFO (46316 on MBP-di-Matteo): something else\n    req: {\n      \"id\": 1,\n      \"method\": \"GET\",\n      \"url\": \"/\",\n      \"headers\": {\n        \"host\": \"localhost:3000\",\n        \"user-agent\": \"curl/7.43.0\",\n        \"accept\": \"*/*\"\n      },\n      \"remoteAddress\": \"::1\",\n      \"remotePort\": 64386\n    }\n[2016-03-31T16:53:21.087Z] INFO (46316 on MBP-di-Matteo): request completed\n    res: {\n      \"statusCode\": 200,\n      \"header\": \"HTTP/1.1 200 OK\\r\\nX-Powered-By: restify\\r\\nContent-Type: text/html; charset=utf-8\\r\\nContent-Length: 11\\r\\nETag: W/\\\"b-XrY7u+Ae7tCTyyK7j1rNww\\\"\\r\\nDate: Thu, 31 Mar 2016 16:53:21 GMT\\r\\nConnection: keep-alive\\r\\n\\r\\n\"\n    }\n    responseTime: 10\n    req: {\n      \"id\": 1,\n      \"method\": \"GET\",\n      \"url\": \"/\",\n      \"headers\": {\n        \"host\": \"localhost:3000\",\n        \"user-agent\": \"curl/7.43.0\",\n        \"accept\": \"*/*\"\n      },\n      \"remoteAddress\": \"::1\",\n      \"remotePort\": 64386\n    }\n```\n\n## API\n\n### pinoHttp([opts], [stream])\n\n`opts`: it has all the options as [pino](http://npm.im/pino) and\n\n* `logger`: parent pino instance for a child logger instance, which will be used by `pino-http`. To refer to this child instance, use [pinoHttp.logger](#pinohttplogger-plogger)\n* `genReqId`: you can pass a function which gets used to generate a request id. The first argument is the request itself. As fallback `pino-http` is just using an integer. This default might not be the desired behavior if you're running multiple instances of the app\n* `useLevel`: the logger level `pino-http` is using to log out the response. default: `info`\n* `customLogLevel`: set to a `function (req, res, err) =\u003e { /* returns level name string */ }`. This function will be invoked to determine the level at which the log should be issued (`silent` will prevent logging). This option is mutually exclusive with the `useLevel` option. The first two arguments are the HTTP request and response. The third argument is an error object if an error has occurred in the request.\n* `autoLogging`: set to `false`, to disable the automatic \"request completed\" and \"request errored\" logging. Defaults to `true`. If set to an object, you can provide more options.\n* `autoLogging.ignore`: set to a `function (req) =\u003e { /* returns boolean */ }`. Useful for defining logic based on req properties (such as a user-agent header) to ignore successful requests.\n* `stream`: same as the second parameter\n* `customReceivedMessage`: set to a `function (req, res) =\u003e { /* returns message string */ }` This function will be invoked at each request received, setting \"msg\" property to returned string. If not set, nothing value will be used.\n* `customReceivedObject`: set to a `function (req, res, loggableObject) =\u003e { /* returns loggable object */ }` This function will be invoked at each request received, replacing the base loggable received object. When set, it is up to the reponsibility of the caller to merge with the `loggableObject` parameter. If not set, default value will be used.\n* `customSuccessMessage`: set to a `function (req, res) =\u003e { /* returns message string */ }` This function will be invoked at each successful response, setting \"msg\" property to returned string. If not set, default value will be used.\n* `customSuccessObject`: set to a `function (req, res, loggableObject) =\u003e { /* returns loggable object */ }` This function will be invoked at each successful response, replacing the base loggable success object. When set, it is up to the reponsibility of the caller to merge with the `loggableObject` parameter. If not set, default value will be used.\n* `customErrorMessage`: set to a `function (req, res, err) =\u003e { /* returns message string */ }` This function will be invoked at each failed response, setting \"msg\" property to returned string. If not set, default value will be used.\n* `customErrorObject`: set to a `function (req, res, err, loggableObject) =\u003e { /* returns loggable object */ }` This function will be invoked at each failed response, the base loggable error object. When set, it is up to the reponsibility of the caller to merge with the `loggableObject` parameter. If not set, default value will be used.\n* `customAttributeKeys`: allows the log object attributes added by `pino-http` to be given custom keys. Accepts an object of format `{ [original]: [override] }`. Attributes available for override are `req`, `res`, `err`, `responseTime` and, when using quietReqLogger, `reqId`.\n* `wrapSerializers`: when `false`, custom serializers will be passed the raw value directly. Defaults to `true`.\n* `customProps`: set to a `function (req, res) =\u003e { /* returns on object */ }` or `{ /* returns on object */ }` This function will be invoked for each request with `req` and `res` where we could pass additional properties that need to be logged outside the `req`.\n* `quietReqLogger`: when `true`, the child logger available on `req.log` will no longer contain the full bindings and will now only have the request id bound at `reqId` (note: the autoLogging messages and the logger available on `res.log` will remain the same except they will also have the additional `reqId` property). default: `false`\n* `quietResLogger`: when `true`, the child logger available on `res.log` will no longer contain the full bindings and will now only have the request id bound at `reqId` (note: the autoLogging message sent on request completion will only contain `req`). default: `false`\n\n`stream`: the destination stream. Could be passed in as an option too.\n\n#### Examples\n\n##### Use as Express middleware\n```js\nconst express = require('express')\nconst logger = require('pino-http')\n\nconst app = express()\n\napp.use(logger())\n\nfunction handle (req, res) {\n  req.log.info('something else')\n  res.end('hello world')\n}\n\napp.listen(3000)\n```\n\n##### Logger options\n\n```js\n'use strict'\n\nconst http = require('http')\nconst server = http.createServer(handle)\nconst { randomUUID } = require('node:crypto')\nconst pino = require('pino')\nconst logger = require('pino-http')({\n  // Reuse an existing logger instance\n  logger: pino(),\n\n  // Define a custom request id function\n  genReqId: function (req, res) {\n    const existingID = req.id ?? req.headers[\"x-request-id\"]\n    if (existingID) return existingID\n    const id = randomUUID()\n    res.setHeader('X-Request-Id', id)\n    return id\n  },\n\n  // Define custom serializers\n  serializers: {\n    err: pino.stdSerializers.err,\n    req: pino.stdSerializers.req,\n    res: pino.stdSerializers.res\n  },\n\n  // Set to `false` to prevent standard serializers from being wrapped.\n  wrapSerializers: true,\n\n  // Logger level is `info` by default\n  useLevel: 'info',\n\n  // Define a custom logger level\n  customLogLevel: function (req, res, err) {\n    if (res.statusCode \u003e= 400 \u0026\u0026 res.statusCode \u003c 500) {\n      return 'warn'\n    } else if (res.statusCode \u003e= 500 || err) {\n      return 'error'\n    } else if (res.statusCode \u003e= 300 \u0026\u0026 res.statusCode \u003c 400) {\n      return 'silent'\n    }\n    return 'info'\n  },\n\n  // Define a custom success message\n  customSuccessMessage: function (req, res) {\n    if (res.statusCode === 404) {\n      return 'resource not found'\n    }\n    return `${req.method} completed`\n  },\n\n  // Define a custom receive message\n  customReceivedMessage: function (req, res) {\n    return 'request received: ' + req.method\n  },\n\n  // Define a custom error message\n  customErrorMessage: function (req, res, err) {\n    return 'request errored with status code: ' + res.statusCode\n  },\n\n  // Override attribute keys for the log object\n  customAttributeKeys: {\n    req: 'request',\n    res: 'response',\n    err: 'error',\n    responseTime: 'timeTaken'\n  },\n\n  // Define additional custom request properties\n  customProps: function (req, res) {\n    return {\n      customProp: req.customProp,\n      // user request-scoped data is in res.locals for express applications\n      customProp2: res.locals.myCustomData\n    }\n  }\n})\n\nfunction handle (req, res) {\n  logger(req, res)\n  req.log.info('something else')\n  res.log.info('just in case you need access to logging when only the response is in scope')\n  res.end('hello world')\n}\n\nserver.listen(3000)\n```\n\n##### Structured Object Hooks\n\nIt is possible to override the default structured object with your own. The hook is provided with the\npino-http base object so that you can merge in your own keys. \n\nThis is useful in scenarios where you want to augment core pino-http logger object with your own event \nlabels.\n\n\u003e If you simply want to change the message which is logged then check out the custom[Received|Error|Success]Message \n\u003e hooks e.g. customReceivedMessage\n\n```js\nconst logger = require('pino-http')({\n  //... remaining config omitted for brevity\n  customReceivedObject: (req, res, val) =\u003e {\n    return {\n      category: 'ApplicationEvent',\n      eventCode: 'REQUEST_RECEIVED'\n    };\n  },\n\n  customSuccessObject: (req, res, val) =\u003e {\n    return {\n      ...val,\n      category: 'ApplicationEvent',\n      eventCode:\n        res.statusCode \u003c 300\n          ? 'REQUEST_PROCESSED'\n          : 'REQUEST_FAILED'\n    };\n  },\n\n  customErrorObject: (req, res, error, val) =\u003e {\n    const store = storage.getStore();\n    const formattedBaggage = convertBaggageToObject(store?.baggage);\n\n    return {\n      ...val,\n      category: 'ApplicationEvent',\n      eventCode: 'REQUEST_FAILED'\n    };\n  }\n\n  // ...remaining config omitted for brevity\n})\n```\n\n##### PinoHttp.logger (P.Logger)\n\nThe `pinoHttp` instance has a property `logger`, which references to an actual logger instance, used\nby pinoHttp. This instance will be a child of an instance, passed as `opts.logger`, or a fresh one,\nif no `opts.logger` is passed. It can be used, for example, for doing most of the things, possible\nto do with any `pino` instance, for example changing logging level in runtime, like so:\n\n```js\nconst pinoHttp = require('pinoHttp')();\npinoHttp.logger.level = 'silent';\n```\n\n##### pinoHttp.startTime (Symbol)\n\nThe `pinoHttp` function has a property called `startTime` which contains a symbol\nthat is used to attach and reference a start time on the HTTP `res` object. If the function\nreturned from `pinoHttp` is not *the first* function to be called in an HTTP servers request\nlistener function then the `responseTime` key in the log output will be offset by any\nprocessing that happens before a response is logged. This can be corrected by manually attaching\nthe start time to the `res` object with the `pinoHttp.startTime` symbol, like so:\n\n```js\nconst http = require('http')\nconst logger = require('pino-http')()\nconst someImportantThingThatHasToBeFirst = require('some-important-thing')\nhttp.createServer((req, res) =\u003e {\n  res[logger.startTime] = Date.now()\n  someImportantThingThatHasToBeFirst(req, res)\n  logger(req, res)\n  res.log.info('log is available on both req and res');\n  res.end('hello world')\n}).listen(3000)\n```\n\n##### Custom formatters\n\nYou can customize the format of the log output by passing a [Pino transport](https://github.com/pinojs/pino/blob/master/docs/transports.md#v7-transports).\n\n```js\nconst logger = require('pino-http')({\n  quietReqLogger: true, // turn off the default logging output\n  transport: {\n    target: 'pino-http-print', // use the pino-http-print transport and its formatting output\n    options: {\n      destination: 1,\n      all: true,\n      translateTime: true\n    }\n  }\n})\n```\n\n\n#### Default serializers\n\n##### pinoHttp.stdSerializers.req\n\nGenerates a JSONifiable object from the HTTP `request` object passed to\nthe `createServer` callback of Node's HTTP server.\n\nIt returns an object in the form:\n\n```js\n{\n  pid: 93535,\n  hostname: 'your host',\n  level: 30,\n  msg: 'my request',\n  time: '2016-03-07T12:21:48.766Z',\n  v: 0,\n  req: {\n    id: 42,\n    method: 'GET',\n    url: '/',\n    headers: {\n      host: 'localhost:50201',\n      connection: 'close'\n    },\n    remoteAddress: '::ffff:127.0.0.1',\n    remotePort: 50202\n  }\n}\n```\n\n##### pinoHttp.stdSerializers.res\n\nGenerates a JSONifiable object from the HTTP `response` object passed to\nthe `createServer` callback of Node's HTTP server.\n\nIt returns an object in the form:\n\n```js\n{\n  pid: 93581,\n  hostname: 'myhost',\n  level: 30,\n  msg: 'my response',\n  time: '2016-03-07T12:23:18.041Z',\n  v: 0,\n  res: {\n    statusCode: 200,\n    header: 'HTTP/1.1 200 OK\\r\\nDate: Mon, 07 Mar 2016 12:23:18 GMT\\r\\nConnection: close\\r\\nContent-Length: 5\\r\\n\\r\\n'\n  }\n}\n```\n\n#### Custom serializers\n\nEach of the standard serializers can be extended by supplying a corresponding\ncustom serializer. For example, let's assume the `request` object has custom\nproperties attached to it, and that all of the custom properties are prefixed\nby `foo`. In order to show these properties, along with the standard serialized\nproperties, in the resulting logs, we can supply a serializer like:\n\n```js\nconst logger = require('pino-http')({\n  serializers: {\n    req (req) {\n      Object.keys(req.raw).forEach((k) =\u003e {\n        if (k.startsWith('foo')) {\n          req[k] = req.raw[k]\n        }\n      })\n      return req\n    }\n  }\n})\n```\n\nIf you prefer to work with the raw value directly, or you want to honor the custom\nserializers already defined by `opts.logger`, you can pass in `opts.wrapSerializers`\nas `false`:\n\n```js\nconst logger = require('pino-http')({\n  wrapSerializers: false,\n  serializers: {\n    req (req) {\n      // `req` is the raw `IncomingMessage` object, not the already serialized request from `pino.stdSerializers.req`.\n      return {\n        message: req.foo\n      };\n    }\n  }\n})\n```\n\n##### Logging request body\n\nLogging of requests' bodies is disabled by default since it can cause security risks such as having private user information (password, other GDPR-protected data, etc.) logged (and persisted in most setups). However if enabled, sensitive information can be redacted as per [redaction documentation](http://getpino.io/#/docs/redaction).\n\nFurthermore, logging more bytes does slow down throughput. [This video by pino maintainers Matteo Collina \u0026 David Mark Clements](https://www.youtube.com/watch?v=zja-_IYNrFc\u0026feature=youtu.be) goes into this in more detail.\n\nAfter considering these factors, logging of the request body can be achieved as follows:\n\n```js\nconst http = require('http')\nconst logger = require('pino-http')({\n  serializers: {\n    req(req) {\n      req.body = req.raw.body;\n      return req;\n    },\n  },\n});\n```\n\n##### Custom serializers + custom log attribute keys\n\nIf custom attribute keys for `req`, `res`, or `err` log keys have been provided, serializers will be applied with the following order of precedence:\n\n`serializer matching custom key` \u003e `serializer matching default key` \u003e `default pino serializer`\n\n## Team\n\n### Matteo Collina\n\n\u003chttps://github.com/mcollina\u003e\n\n\u003chttps://www.npmjs.com/~matteo.collina\u003e\n\n\u003chttps://twitter.com/matteocollina\u003e\n\n\n### David Mark Clements\n\n\u003chttps://github.com/davidmarkclements\u003e\n\n\u003chttps://www.npmjs.com/~davidmarkclements\u003e\n\n\u003chttps://twitter.com/davidmarkclem\u003e\n\n\u003ca name=\"acknowledgements\"\u003e\u003c/a\u003e\n## Acknowledgements\n\nThis project was kindly sponsored by [nearForm](http://nearform.com).\n\nLogo and identity designed by Beibhinn Murphy O'Brien: https://www.behance.net/BeibhinnMurphyOBrien.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpinojs%2Fpino-http","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpinojs%2Fpino-http","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpinojs%2Fpino-http/lists"}