{"id":20098287,"url":"https://github.com/zemd/nestjs-pino-logger","last_synced_at":"2025-05-06T05:32:01.747Z","repository":{"id":65946315,"uuid":"601166124","full_name":"zemd/nestjs-pino-logger","owner":"zemd","description":"Nestjs logger based on pino","archived":false,"fork":false,"pushed_at":"2025-04-22T05:38:09.000Z","size":1711,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-27T23:01:55.984Z","etag":null,"topics":["logging","nestjs","nestjs-module","pino"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zemd.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.md","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},"funding":{"github":["zemd"],"buy_me_a_coffee":"zemd"}},"created_at":"2023-02-13T14:03:43.000Z","updated_at":"2025-04-22T05:38:12.000Z","dependencies_parsed_at":"2024-11-17T05:34:10.959Z","dependency_job_id":"4ea159f7-0a8f-4705-96cf-dd7b42766bc9","html_url":"https://github.com/zemd/nestjs-pino-logger","commit_stats":{"total_commits":18,"total_committers":2,"mean_commits":9.0,"dds":0.05555555555555558,"last_synced_commit":"8855e533cd3197c11cf96aa134408155bcfc05df"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zemd%2Fnestjs-pino-logger","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zemd%2Fnestjs-pino-logger/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zemd%2Fnestjs-pino-logger/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zemd%2Fnestjs-pino-logger/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zemd","download_url":"https://codeload.github.com/zemd/nestjs-pino-logger/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252629176,"owners_count":21779160,"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":["logging","nestjs","nestjs-module","pino"],"created_at":"2024-11-13T17:02:38.339Z","updated_at":"2025-05-06T05:32:01.734Z","avatar_url":"https://github.com/zemd.png","language":"TypeScript","funding_links":["https://github.com/sponsors/zemd","https://buymeacoffee.com/zemd"],"categories":[],"sub_categories":[],"readme":"[![npm](https://img.shields.io/npm/v/@zemd/nestjs-pino-logger?color=0000ff\u0026label=npm\u0026labelColor=000)](https://npmjs.com/package/@zemd/nestjs-pino-logger)\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"http://nestjs.com/\" target=\"blank\"\u003e\u003cimg src=\"https://nestjs.com/img/logo-small.svg\" width=\"200\" alt=\"Nest Logo\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003eA progressive \u003ca href=\"http://nodejs.org\" target=\"_blank\"\u003eNode.js\u003c/a\u003e framework for building efficient and scalable server-side applications.\u003c/p\u003e\n\n# Nestjs-Pino-Logger\n\n## Motivation\n\nThe motivation of this package is to provide a pino logger for nestjs framework with near identical behaviour of the standard nestjs logger and to provide a good defaults for the logger. This also means that pino logger can be customized as needed without need to configure the Logger service, that makes the package flexible and lightweight.\n\n## Installation\n\n```bash\nnpm install --save @zemd/nestjs-pino-logger\n```\n\nyou also might need:\n\n```bash\nnpm install --save @nestjs/common @nestjs/config pino pino-http\nnpm install --save-dev pino-pretty\n```\n\n## Usage\n\nlet's say you are defining AppModule:\n\n```typescript\n// app.module.ts\nimport { LoggerModule } from \"@zemd/nestjs-pino-logger\";\nimport { ConfigModule } from \"@nestjs/config\";\nimport pinoConfig from \"./config/pino.config\";\nimport pinoHttpConfig from \"./config/pino-http.config\";\n\n@Module({\n  imports: [\n    ConfigModule.forRoot({\n      isGlobal: true,\n      load: [pinoConfig, pinoHttpConfig],\n      // ... any other configuration options you want to use\n    }),\n    LoggerModule.forRootAsync({\n      imports: [ConfigModule],\n      useFactory: (configService: ConfigService) =\u003e configService.get\u003cLoggerOptions\u003e(\"pino\"),\n      inject: [ConfigService],\n    }),\n  ],\n})\nclass AppModule {}\n```\n\nthen you should inject `Logger` into your main app:\n\n```typescript\n// main.ts\nimport { Logger, PINO_LOGGER_INSTANCE } from \"@zemd/nestjs-pino-logger\";\nimport pinoHttp from \"pino-http\";\nimport type { Options } from \"pino-http\";\n\nconst app = await NestFactory.create(AppModule, { bufferLogs: true });\napp.useLogger(app.get(Logger));\n\n// if you want to use pino-http ↓\napp.use(\n  pinoHttp({\n    ...configService.get\u003cOptions\u003e(\"pino-http\"),\n    logger: app.get(PINO_LOGGER_INSTANCE),\n  }),\n);\n```\n\nnow let's look closer at the `pino.config.ts` and `pino-http.config.ts` files:\n\n```typescript\n// pino.config.ts\nimport { registerAs } from \"@nestjs/config\";\nimport pino from \"pino\";\nimport { customLevels } from \"@zemd/nestjs-pino-logger\";\n\nexport default registerAs(\"pino\", (): Partial\u003cLoggerOptions\u003e =\u003e {\n  const targets: pino.TransportTargetOptions\u003cRecord\u003cstring, any\u003e\u003e[] = [];\n\n  if (process.env.NODE_ENV !== \"production\") {\n    // you don't need to use this transport in production, usually you would want to send logs as json object to the observability service\n    targets.push({\n      target: \"@zemd/nestjs-pino-logger/dist/pino-pretty-transport.js\",\n      level: process.env.NODE_ENV === \"development\" ? \"verbose\" : \"error\",\n      options: {\n        colorize: false,\n        translateTime: true,\n        include: \"\",\n        singleLine: false,\n        hideObject: true,\n      },\n    });\n  }\n\n  const transport = targets.length ? { targets } : undefined;\n\n  return {\n    customLevels,\n    useOnlyCustomLevels: true, // this is required! and you should use this config explicitly to avoid any unexpected behaviour\n    transport,\n  };\n});\n```\n\nthat's basically it, if you want to use pino logger in your nestjs application, you can use any kind on configuration, but at the same time you can use default nestjs logger.\n\n```typescript\n// pino-http.config.ts\nimport { registerAs } from \"@nestjs/config\";\nimport { Options } from \"pino-http\";\nimport * as crypto from \"node:crypto\";\n\nexport default registerAs(\"pino-http\", (): Partial\u003cOptions\u003e =\u003e {\n  return {\n    level: process.env.LOG_LEVEL ?? \"verbose\",\n    genReqId: (req, res) =\u003e {\n      // this is not required\n      if (req.id) {\n        return req.id;\n      }\n      return crypto.randomUUID();\n    },\n    customProps: () =\u003e ({\n      // by default there is no \"context\" provided inside the middleware, so we need to add it manually\n      context: \"http\",\n    }),\n    // pino-http types don't accept custom log levels, so we need to use ts-ignore here,\n    // if you want to use one log level for all requests, just use `useLevel` option.\n    // @ts-ignore\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 \"verbose\";\n      }\n      return \"log\";\n    },\n  };\n});\n```\n\n## Advanced usage\n\n### Extending message object\n\nIn case if you want extend pino log object by adding more fields, you can use `buildPinoMessage` helper function, that adds hidden `Symbol` to the object and which allows to distinguish between regular log message and custom.\n\nIt looks like:\n\n```typescript\nconst message = buildPinoMessage({\n  message: \"Hello World %o\",\n  mergingObject: { foo: \"bar\" },\n  interpolationValues: [{ data: \"the data object will be used to format the message\" }],\n});\nthis.logger.log(message, \"here you can also pass something that will be added to the msg string\");\n```\n\n### Using request id\n\n`nestjs-pino` package has exceptional feature that allows to leverage the request-id that you might noticed previously when we added `pino-http` middleware. Essentially this is great feature, but at the moment I don't think that it should be implemented within this package anytime in the future. It can be achieved by storing this info using `AsyncLocalStorage` inside the middleware function in `main.ts` file, and then retrieved in pino mixin. But in more complex scenario you should be using open telemetry, which can handle global trace-id and pass it with every log message. Also having request object in each log message would increase it's size and make it more expensive to transfer and store.\n\nExample of using open telemetry:\n\n```typescript\n// pino.config.ts\nimport { context, isSpanContextValid, trace } from \"@opentelemetry/api\";\n\n//...registerAs...\nreturn {\n  // ... transfer\n  // ... other pino options\n  mixin: () =\u003e {\n    const record = {};\n    const span = trace.getSpan(context.active());\n    if (span) {\n      const spanContext = span.spanContext();\n\n      if (isSpanContextValid(spanContext)) {\n        Object.assign(record, {\n          trace_id: spanContext.traceId,\n          span_id: spanContext.spanId,\n          trace_flags: `0${spanContext.traceFlags.toString(16)}`,\n        });\n      }\n    }\n\n    return record;\n  },\n};\n//...\n```\n\n`@opentelemetry/api` package also uses `AsyncLocalStorage` under the hood, so as you can see having control over the pino configuration can allow to achieve any kind of task.\n\n## Example\n\n![logging example](example.png \"Nestjs-Pino-Logger example\")\n\n## License\n\n`@zemd/nestjs-pino-logger` released under the [BlueOak-1.0.0](https://blueoakcouncil.org/license/1.0.0).\n\n## Donate\n\n[![](https://img.shields.io/static/v1?label=UNITED24\u0026message=support%20Ukraine\u0026color=blue)](https://u24.gov.ua/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzemd%2Fnestjs-pino-logger","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzemd%2Fnestjs-pino-logger","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzemd%2Fnestjs-pino-logger/lists"}