Showing preview only (702K chars total). Download the full file or copy to clipboard to get everything.
Repository: pinojs/pino
Branch: main
Commit: 826751e425b3
Files: 207
Total size: 651.8 KB
Directory structure:
gitextract_1hdwcln0/
├── .github/
│ ├── dependabot.yml
│ └── workflows/
│ ├── bench.yml
│ ├── ci.yml
│ ├── lock-threads.yml
│ ├── publish-release.yml
│ └── target-main.yml
├── .gitignore
├── .nojekyll
├── .npmignore
├── .npmrc
├── .prettierignore
├── CNAME
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── SECURITY.md
├── benchmarks/
│ ├── basic.bench.js
│ ├── child-child.bench.js
│ ├── child-creation.bench.js
│ ├── child.bench.js
│ ├── deep-object.bench.js
│ ├── formatters.bench.js
│ ├── internal/
│ │ ├── custom-levels.js
│ │ ├── just-pino-heavy.bench.js
│ │ ├── just-pino.bench.js
│ │ ├── parent-vs-child.bench.js
│ │ └── redact.bench.js
│ ├── long-string.bench.js
│ ├── multi-arg.bench.js
│ ├── multistream.js
│ ├── object.bench.js
│ └── utils/
│ ├── generate-benchmark-doc.js
│ ├── runbench.js
│ └── wrap-log-level.js
├── bin.js
├── browser.js
├── build/
│ └── sync-version.js
├── docs/
│ ├── api.md
│ ├── asynchronous.md
│ ├── benchmarks.md
│ ├── browser.md
│ ├── bundling.md
│ ├── child-loggers.md
│ ├── diagnostics.md
│ ├── ecosystem.md
│ ├── help.md
│ ├── lts.md
│ ├── pretty.md
│ ├── redaction.md
│ ├── transports.md
│ └── web.md
├── docsify/
│ └── sidebar.md
├── eslint.config.js
├── examples/
│ ├── basic.js
│ └── transport.js
├── file.js
├── inc-version.sh
├── index.html
├── lib/
│ ├── caller.js
│ ├── constants.js
│ ├── deprecations.js
│ ├── levels.js
│ ├── meta.js
│ ├── multistream.js
│ ├── proto.js
│ ├── redaction.js
│ ├── symbols.js
│ ├── time.js
│ ├── tools.js
│ ├── transport-stream.js
│ ├── transport.js
│ └── worker.js
├── package.json
├── pino.d.ts
├── pino.js
├── test/
│ ├── basic.test.js
│ ├── broken-pipe.test.js
│ ├── browser-child.test.js
│ ├── browser-disabled.test.js
│ ├── browser-early-console-freeze.test.js
│ ├── browser-is-level-enabled.test.js
│ ├── browser-levels.test.js
│ ├── browser-redaction.test.js
│ ├── browser-serializers.test.js
│ ├── browser-timestamp.test.js
│ ├── browser-transmit.test.js
│ ├── browser.test.js
│ ├── complex-objects.test.js
│ ├── crlf.test.js
│ ├── custom-levels.test.js
│ ├── diagnostics.test.js
│ ├── error-key.test.js
│ ├── error.test.js
│ ├── escaping.test.js
│ ├── esm/
│ │ ├── esm.mjs
│ │ ├── index.test.js
│ │ └── named-exports.mjs
│ ├── exit.test.js
│ ├── fixtures/
│ │ ├── broken-pipe/
│ │ │ ├── basic.js
│ │ │ ├── destination.js
│ │ │ └── syncfalse.js
│ │ ├── console-transport.js
│ │ ├── crashing-transport.js
│ │ ├── default-exit.js
│ │ ├── destination-exit.js
│ │ ├── eval/
│ │ │ ├── index.js
│ │ │ └── node_modules/
│ │ │ ├── 14-files.js
│ │ │ ├── 2-files.js
│ │ │ ├── file1.js
│ │ │ ├── file10.js
│ │ │ ├── file11.js
│ │ │ ├── file12.js
│ │ │ ├── file13.js
│ │ │ ├── file14.js
│ │ │ ├── file2.js
│ │ │ ├── file3.js
│ │ │ ├── file4.js
│ │ │ ├── file5.js
│ │ │ ├── file6.js
│ │ │ ├── file7.js
│ │ │ ├── file8.js
│ │ │ └── file9.js
│ │ ├── noop-transport.js
│ │ ├── pretty/
│ │ │ └── null-prototype.js
│ │ ├── stdout-hack-protection.js
│ │ ├── syncfalse-child.js
│ │ ├── syncfalse-exit.js
│ │ ├── syncfalse-flush-exit.js
│ │ ├── syncfalse.js
│ │ ├── syntax-error-esm.mjs
│ │ ├── to-file-transport-with-transform.js
│ │ ├── to-file-transport.js
│ │ ├── to-file-transport.mjs
│ │ ├── transport/
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ ├── transport-exit-immediately-with-async-dest.js
│ │ ├── transport-exit-immediately.js
│ │ ├── transport-exit-on-ready.js
│ │ ├── transport-invalid-node-options.js
│ │ ├── transport-main.js
│ │ ├── transport-many-lines.js
│ │ ├── transport-preload-main.mjs
│ │ ├── transport-preload.mjs
│ │ ├── transport-string-stdout.js
│ │ ├── transport-transform.js
│ │ ├── transport-uses-pino-config.js
│ │ ├── transport-with-on-exit.js
│ │ ├── transport-worker-data.js
│ │ ├── transport-worker-name.js
│ │ ├── transport-worker.js
│ │ ├── transport-wrong-export-type.js
│ │ └── ts/
│ │ ├── to-file-transport-native.mts
│ │ ├── to-file-transport-with-transform.ts
│ │ ├── to-file-transport.ts
│ │ ├── transpile.cjs
│ │ ├── transport-exit-immediately-with-async-dest.ts
│ │ ├── transport-exit-immediately.ts
│ │ ├── transport-exit-on-ready.ts
│ │ ├── transport-main.ts
│ │ ├── transport-string-stdout.ts
│ │ └── transport-worker.ts
│ ├── formatters.test.js
│ ├── helper.d.ts
│ ├── helper.js
│ ├── hooks.test.js
│ ├── http.test.js
│ ├── internals/
│ │ └── version.test.js
│ ├── is-level-enabled.test.js
│ ├── jest/
│ │ └── basic.spec.js
│ ├── levels.test.js
│ ├── metadata.test.js
│ ├── mixin-merge-strategy.test.js
│ ├── mixin.test.js
│ ├── multistream.test.js
│ ├── redact.test.js
│ ├── serializers.test.js
│ ├── stdout-protection.test.js
│ ├── syncfalse.test.js
│ ├── timestamp-nano.test.js
│ ├── timestamp.test.js
│ ├── transport/
│ │ ├── big.test.js
│ │ ├── bundlers-support.test.js
│ │ ├── caller.test.js
│ │ ├── core.test.js
│ │ ├── core.transpiled.test.ts
│ │ ├── crash.test.js
│ │ ├── module-link.test.js
│ │ ├── native-type-stripping.test.mjs
│ │ ├── node-options.test.js
│ │ ├── pipeline.test.js
│ │ ├── preload.test.js
│ │ ├── repl.test.js
│ │ ├── sync-false.test.js
│ │ ├── sync-true.test.js
│ │ ├── targets.test.js
│ │ └── uses-pino-config.test.js
│ ├── transport-stream.test.js
│ └── types/
│ ├── pino-import.test-d.cts
│ ├── pino-multistream.test-d.ts
│ ├── pino-top-export.test-d.ts
│ ├── pino-transport.test-d.ts
│ ├── pino-type-only.test-d.ts
│ ├── pino.test-d.ts
│ ├── pino.ts
│ └── tsconfig.json
├── tsconfig.json
└── tstyche.config.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
open-pull-requests-limit: 10
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "monthly"
open-pull-requests-limit: 10
================================================
FILE: .github/workflows/bench.yml
================================================
name: Benchmarks
on:
push:
branches:
- main
paths-ignore:
- 'docs/**'
- '*.md'
pull_request:
paths-ignore:
- 'docs/**'
- '*.md'
permissions:
contents: read
jobs:
benchmark_current:
name: benchmark current
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v6.0.2
with:
ref: ${{ github.base_ref }}
persist-credentials: false
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: lts/*
- name: Install Modules
run: npm i --ignore-scripts
- name: Run Benchmark
run: npm run bench | tee current.txt
- name: Upload Current Results
uses: actions/upload-artifact@v4
with:
name: current
path: current.txt
benchmark_branch:
name: benchmark branch
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v6.0.2
with:
persist-credentials: false
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: lts/*
- name: Install Modules
run: npm i --ignore-scripts
- name: Run Benchmark
run: npm run bench | tee branch.txt
- name: Upload Branch Results
uses: actions/upload-artifact@v4
with:
name: branch
path: branch.txt
================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on:
push:
branches:
- main
- 'v*'
paths-ignore:
- 'docs/**'
- '*.md'
pull_request:
paths-ignore:
- 'docs/**'
- '*.md'
# This allows a subsequently queued workflow run to interrupt previous runs
concurrency:
group: "${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}"
cancel-in-progress: true
jobs:
dependency-review:
name: Dependency Review
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Check out repo
uses: actions/checkout@v6.0.2
with:
persist-credentials: false
- name: Dependency review
uses: actions/dependency-review-action@v4
test:
name: ${{ matrix.node-version }} ${{ matrix.os }}
runs-on: ${{ matrix.os }}
permissions:
contents: read
strategy:
fail-fast: false
matrix:
os: [macOS-latest, windows-latest, ubuntu-latest]
node-version: [20, 22, 24, 25]
exclude:
- os: windows-latest
node-version: 22
steps:
- name: Check out repo
uses: actions/checkout@v6.0.2
with:
persist-credentials: false
- name: Setup Node ${{ matrix.node-version }}
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
run: npm i --ignore-scripts
- name: Run tests
run: npm run test-ci
- name: Run smoke test
if: >
matrix.os != 'windows-latest' &&
matrix.node-version > 14
run: npm run test:smoke
automerge:
name: Automerge Dependabot PRs
if: >
github.event_name == 'pull_request' &&
github.event.pull_request.user.login == 'dependabot[bot]'
needs: test
permissions:
pull-requests: write
contents: write
runs-on: ubuntu-latest
steps:
- uses: fastify/github-action-merge-dependabot@v3
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
exclude: 'sonic-boom,pino-std-serializers,quick-format-unescaped,fast-redact'
================================================
FILE: .github/workflows/lock-threads.yml
================================================
name: 'Lock Threads'
on:
schedule:
- cron: '0 0 * * *'
workflow_dispatch:
permissions:
issues: write
pull-requests: write
concurrency:
group: lock
jobs:
action:
runs-on: ubuntu-latest
steps:
- uses: jsumners/lock-threads@b27edac0ac998d42b2815e122b6c24b32b568321
with:
log-output: true
issue-inactive-days: '30'
issue-comment: >
This issue has been automatically locked since there
has not been any recent activity after it was closed.
Please open a new issue for related bugs.
pr-comment: >
This pull request has been automatically locked since there
has not been any recent activity after it was closed.
Please open a new issue for related bugs.
================================================
FILE: .github/workflows/publish-release.yml
================================================
name: Publish release
on:
workflow_dispatch:
inputs:
version:
description: 'The version number to tag and release'
required: true
type: string
prerelease:
description: 'Release as pre-release'
required: false
type: boolean
default: false
jobs:
release-npm:
runs-on: ubuntu-latest
environment: main
permissions:
contents: write
id-token: write
steps:
- uses: actions/checkout@0c366fd6a839edf440554fa01a7085ccba70ac98 # v4
- uses: actions/setup-node@v6
with:
node-version: '22'
registry-url: 'https://registry.npmjs.org'
- run: npm install npm -g
- run: npm install
- name: Change version number and sync
run: |
node build/sync-version.js ${{ inputs.version }}
- name: GIT commit and push all changed files
run: |
git config --global user.name "mcollina"
git config --global user.email "hello@matteocollina.com"
git commit -n -a -m "Bumped v${{ inputs.version }}"
git push origin HEAD:${{ github.ref }}
- run: npm publish --access public --tag ${{ inputs.prerelease == true && 'next' || 'latest' }}
- name: 'Create release notes'
run: |
npx @matteo.collina/release-notes -a ${{ secrets.GITHUB_TOKEN }} -t v${{ inputs.version }} -r pino -o pinojs ${{ github.event.inputs.prerelease == 'true' && '-p' || '' }} -c ${{ github.ref }}
================================================
FILE: .github/workflows/target-main.yml
================================================
name: PR Target Check
on:
pull_request_target:
types: [opened]
permissions:
pull-requests: write
jobs:
comment:
if: ${{ github.base_ref != "master" }}
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v8
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '⚠️ This pull request does not target the master branch.'
})
================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
# Vim swap files
*.swp
# macOS files
.DS_Store
# editor files
.vscode
.idea
# lock files
package-lock.json
pnpm-lock.yaml
yarn.lock
# 0x
.__browserify_string_empty.js
profile-*
# Generated files
test/fixtures/ts/*js
!test/fixtures/eval/node_modules
!test/fixtures/ts/transpile.cjs
# AI assistants
CLAUDE.md
.claude
.agents
.pi
AGENTS.md
================================================
FILE: .nojekyll
================================================
================================================
FILE: .npmignore
================================================
*.png
.github/
docsify/
.nyc_output/
coverage/
================================================
FILE: .npmrc
================================================
package-lock=false
================================================
FILE: .prettierignore
================================================
*
================================================
FILE: CNAME
================================================
getpino.io
================================================
FILE: CONTRIBUTING.md
================================================
# Pino is an OPEN Open Source Project
## What?
Individuals making significant and valuable contributions are given commit-access to the project to contribute as they see fit. This project is more like an open wiki than a standard guarded open source project.
## Rules
Before you start coding, please read [Contributing to projects with git](https://jrfom.com/posts/2017/03/08/a-primer-on-contributing-to-projects-with-git/).
Notice that as long as you don't have commit-access to the project, you have to fork the project and open PRs from the feature branches of the forked project.
There are a few basic ground-rules for contributors:
1. **No `--force` pushes** on `main` or modifying the Git history in any way after a PR has been merged.
1. **Non-main branches** ought to be used for ongoing work.
1. **Non-trivial changes** ought to be subject to an **internal pull-request** to solicit feedback from other contributors.
1. All pull-requests for new features **must** target the `main` branch. PRs to fix bugs in LTS releases are also allowed.
1. Contributors should attempt to adhere to the prevailing code-style.
1. 100% code coverage
## Releases
Declaring formal releases remains the prerogative of the project maintainer.
## Changes to this arrangement
This is an experiment and feedback is welcome! This document may also be subject to pull-requests or changes by contributors where you believe you have something valuable to add or change.
-----------------------------------------
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2016-2025 Matteo Collina, David Mark Clements and the Pino contributors listed at <https://github.com/pinojs/pino#the-team> and in the README file.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================

# pino
[](https://www.npmjs.com/package/pino)
[](https://github.com/pinojs/pino/actions)
[](https://standardjs.com/)
[Very low overhead](#low-overhead) JavaScript logger.
## Documentation
* [Benchmarks ⇗](/docs/benchmarks.md)
* [API ⇗](/docs/api.md)
* [Browser API ⇗](/docs/browser.md)
* [Redaction ⇗](/docs/redaction.md)
* [Child Loggers ⇗](/docs/child-loggers.md)
* [Transports ⇗](/docs/transports.md)
* [Diagnostics ⇗](/docs/diagnostics.md)
* [Web Frameworks ⇗](/docs/web.md)
* [Pretty Printing ⇗](/docs/pretty.md)
* [Asynchronous Logging ⇗](/docs/asynchronous.md)
* [Ecosystem ⇗](/docs/ecosystem.md)
* [Help ⇗](/docs/help.md)
* [Long Term Support Policy ⇗](/docs/lts.md)
## Runtimes
### Node.js
Pino is built to run on [Node.js](http://nodejs.org).
### Bare
Pino works on [Bare](https://github.com/holepunchto/bare) with the [`pino-bare`](https://github.com/pinojs/pino-bare) compatability module.
### Pear
Pino works on [Pear](https://docs.pears.com), which is built on [Bare](https://github.com/holepunchto/bare), with the [`pino-bare`](https://github.com/pinojs/pino-bare) compatibility module.
## Install
Using NPM:
```
$ npm install pino
```
Using YARN:
```
$ yarn add pino
```
If you would like to install pino v6, refer to https://github.com/pinojs/pino/tree/v6.x.
## Usage
```js
const logger = require('pino')()
logger.info('hello world')
const child = logger.child({ a: 'property' })
child.info('hello child!')
```
This produces:
```
{"level":30,"time":1531171074631,"msg":"hello world","pid":657,"hostname":"Davids-MBP-3.fritz.box"}
{"level":30,"time":1531171082399,"msg":"hello child!","pid":657,"hostname":"Davids-MBP-3.fritz.box","a":"property"}
```
For using Pino with a web framework see:
* [Pino with Fastify](docs/web.md#fastify)
* [Pino with Express](docs/web.md#express)
* [Pino with Hapi](docs/web.md#hapi)
* [Pino with Restify](docs/web.md#restify)
* [Pino with Koa](docs/web.md#koa)
* [Pino with Node core `http`](docs/web.md#http)
* [Pino with Nest](docs/web.md#nest)
* [Pino with Hono](docs/web.md#hono)
<a name="essentials"></a>
## Essentials
### Development Formatting
The [`pino-pretty`](https://github.com/pinojs/pino-pretty) module can be used to
format logs during development:

### Transports & Log Processing
Due to Node's single-threaded event-loop, it's highly recommended that sending,
alert triggering, reformatting, and all forms of log processing
are conducted in a separate process or thread.
In Pino terminology, we call all log processors "transports" and recommend that the
transports be run in a worker thread using our `pino.transport` API.
For more details see our [Transports⇗](docs/transports.md) document.
### Low overhead
Using minimum resources for logging is very important. Log messages
tend to get added over time and this can lead to a throttling effect
on applications – such as reduced requests per second.
In many cases, Pino is over 5x faster than alternatives.
See the [Benchmarks](docs/benchmarks.md) document for comparisons.
### Bundling support
Pino supports being bundled using tools like webpack or esbuild.
See [Bundling](docs/bundling.md) document for more information.
<a name="team"></a>
## The Team
### Matteo Collina
<https://github.com/mcollina>
<https://www.npmjs.com/~matteo.collina>
<https://twitter.com/matteocollina>
### David Mark Clements
<https://github.com/davidmarkclements>
<https://www.npmjs.com/~davidmarkclements>
<https://twitter.com/davidmarkclem>
### James Sumners
<https://github.com/jsumners>
<https://www.npmjs.com/~jsumners>
<https://twitter.com/jsumners79>
### Thomas Watson Steen
<https://github.com/watson>
<https://www.npmjs.com/~watson>
<https://twitter.com/wa7son>
## Contributing
Pino is an **OPEN Open Source Project**. This means that:
> Individuals making significant and valuable contributions are given commit-access to the project to contribute as they see fit. This project is more like an open wiki than a standard guarded open source project.
See the [CONTRIBUTING.md](https://github.com/pinojs/pino/blob/main/CONTRIBUTING.md) file for more details.
<a name="acknowledgments"></a>
## Acknowledgments
This project was kindly sponsored by [nearForm](https://nearform.com).
This project is kindly sponsored by [Platformatic](https://platformatic.dev).
Logo and identity designed by Cosmic Fox Design: https://www.behance.net/cosmicfox.
## License
Licensed under [MIT](./LICENSE).
[elasticsearch]: https://www.elastic.co/products/elasticsearch
[kibana]: https://www.elastic.co/products/kibana
================================================
FILE: SECURITY.md
================================================
# Security Policy
This document describes the management of vulnerabilities for the
Pino project and all modules within the Pino organization.
## The Pino Threat Model
Pino is a fast JSON logger for Node.js. Understanding what Pino considers
a security vulnerability requires understanding its trust boundaries.
Pino's threat model builds upon the
[Node.js threat model](https://github.com/nodejs/node/blob/main/SECURITY.md#the-nodejs-threat-model).
We recommend reading that document first, as Pino inherits its trust assumptions.
Pino trusts the applications using it and the environment that it is running in.
This includes all the application code, the transport, the filesystem and all
non-externally provided input.
Pino assumes all objects being logged, `logger.info(obj, message)`, are json-serializable.
Use the `serializers` and `redact` features to sanitize them.
Pino is not hardened against external prototype pollution attacks, but we
will accept a vulnerability if Pino can be misused to cause a prototype pollution.
## Reporting vulnerabilities
Individuals who find potential vulnerabilities in Pino are invited
to report them via email at matteo.collina@gmail.com.
### Strict measures when reporting vulnerabilities
Avoid creating new "informative" reports. Only create new
report a potential vulnerability if you are absolutely sure this
should be tagged as an actual vulnerability. Be careful on the maintainers time.
## Handling vulnerability reports
When a potential vulnerability is reported, the following actions are taken:
### Triage
**Delay:** 5 business days
Within 5 business days, a member of the security team provides a first answer to the
individual who submitted the potential vulnerability. The possible responses
can be:
* Acceptance: what was reported is considered as a new vulnerability
* Rejection: what was reported is not considered as a new vulnerability
* Need more information: the security team needs more information in order to evaluate what was reported.
Triaging should include updating issue fields:
* Asset - set/create the module affected by the report
* Severity - TBD, currently left empty
### Correction follow-up
**Delay:** 90 days
When a vulnerability is confirmed, a member of the security team volunteers to follow
up on this report.
With the help of the individual who reported the vulnerability, they contact
the maintainers of the vulnerable package to make them aware of the
vulnerability. The maintainers can be invited as participants to the reported issue.
With the package maintainer, they define a release date for the publication
of the vulnerability. Ideally, this release date should not happen before
the package has been patched.
The report's vulnerable versions upper limit should be set to:
* `*` if there is no fixed version available by the time of publishing the report.
* the last vulnerable version. For example: `<=1.2.3` if a fix exists in `1.2.4`
### Publication
**Delay:** 90 days
Within 90 days after the triage date, the vulnerability must be made public.
**Severity**: Vulnerability severity is assessed using [CVSS v.3](https://www.first.org/cvss/user-guide).
If the package maintainer is actively developing a patch, an additional delay
can be added with the approval of the security team and the individual who
reported the vulnerability.
At this point, a CVE will be requested by the team.
================================================
FILE: benchmarks/basic.bench.js
================================================
'use strict'
const bench = require('fastbench')
const pino = require('../')
const bunyan = require('bunyan')
const bole = require('bole')('bench')
const winston = require('winston')
const fs = require('node:fs')
const dest = fs.createWriteStream('/dev/null')
const loglevel = require('./utils/wrap-log-level')(dest)
const plogNodeStream = pino(dest)
delete require.cache[require.resolve('../')]
const plogMinLength = require('../')(pino.destination({ dest: '/dev/null', minLength: 4096 }))
delete require.cache[require.resolve('../')]
const plogDest = require('../')(pino.destination('/dev/null'))
process.env.DEBUG = 'dlog'
const debug = require('debug')
const dlog = debug('dlog')
dlog.log = function (s) { dest.write(s) }
const max = 10
const blog = bunyan.createLogger({
name: 'myapp',
streams: [{
level: 'trace',
stream: dest
}]
})
require('bole').output({
level: 'info',
stream: dest
}).setFastTime(true)
const chill = winston.createLogger({
transports: [
new winston.transports.Stream({
stream: fs.createWriteStream('/dev/null')
})
]
})
const run = bench([
function benchBunyan (cb) {
for (var i = 0; i < max; i++) {
blog.info('hello world')
}
setImmediate(cb)
},
function benchWinston (cb) {
for (var i = 0; i < max; i++) {
chill.log('info', 'hello world')
}
setImmediate(cb)
},
function benchBole (cb) {
for (var i = 0; i < max; i++) {
bole.info('hello world')
}
setImmediate(cb)
},
function benchDebug (cb) {
for (var i = 0; i < max; i++) {
dlog('hello world')
}
setImmediate(cb)
},
function benchLogLevel (cb) {
for (var i = 0; i < max; i++) {
loglevel.info('hello world')
}
setImmediate(cb)
},
function benchPino (cb) {
for (var i = 0; i < max; i++) {
plogDest.info('hello world')
}
setImmediate(cb)
},
function benchPinoMinLength (cb) {
for (var i = 0; i < max; i++) {
plogMinLength.info('hello world')
}
setImmediate(cb)
},
function benchPinoNodeStream (cb) {
for (var i = 0; i < max; i++) {
plogNodeStream.info('hello world')
}
setImmediate(cb)
}
], 10000)
run(run)
================================================
FILE: benchmarks/child-child.bench.js
================================================
'use strict'
const bench = require('fastbench')
const pino = require('../')
const bunyan = require('bunyan')
const fs = require('node:fs')
const dest = fs.createWriteStream('/dev/null')
const plogNodeStream = pino(dest).child({ a: 'property' }).child({ sub: 'child' })
delete require.cache[require.resolve('../')]
const plogDest = require('../')(pino.destination('/dev/null')).child({ a: 'property' }).child({ sub: 'child' })
delete require.cache[require.resolve('../')]
const plogMinLength = require('../')(pino.destination({ dest: '/dev/null', sync: false, minLength: 4096 }))
.child({ a: 'property' })
.child({ sub: 'child' })
const max = 10
const blog = bunyan.createLogger({
name: 'myapp',
streams: [{
level: 'trace',
stream: dest
}]
}).child({ a: 'property' }).child({ sub: 'child' })
const run = bench([
function benchBunyanChildChild (cb) {
for (var i = 0; i < max; i++) {
blog.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoChildChild (cb) {
for (var i = 0; i < max; i++) {
plogDest.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoMinLengthChildChild (cb) {
for (var i = 0; i < max; i++) {
plogMinLength.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoNodeStreamChildChild (cb) {
for (var i = 0; i < max; i++) {
plogNodeStream.info({ hello: 'world' })
}
setImmediate(cb)
}
], 10000)
run(run)
================================================
FILE: benchmarks/child-creation.bench.js
================================================
'use strict'
const bench = require('fastbench')
const pino = require('../')
const bunyan = require('bunyan')
const bole = require('bole')('bench')
const fs = require('node:fs')
const dest = fs.createWriteStream('/dev/null')
const plogNodeStream = pino(dest)
const plogDest = pino(pino.destination(('/dev/null')))
delete require.cache[require.resolve('../')]
const plogMinLength = require('../')(pino.destination({ dest: '/dev/null', sync: false, minLength: 4096 }))
const max = 10
const blog = bunyan.createLogger({
name: 'myapp',
streams: [{
level: 'trace',
stream: dest
}]
})
require('bole').output({
level: 'info',
stream: dest
}).setFastTime(true)
const run = bench([
function benchBunyanCreation (cb) {
const child = blog.child({ a: 'property' })
for (var i = 0; i < max; i++) {
child.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchBoleCreation (cb) {
const child = bole('child')
for (var i = 0; i < max; i++) {
child.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoCreation (cb) {
const child = plogDest.child({ a: 'property' })
for (var i = 0; i < max; i++) {
child.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoMinLengthCreation (cb) {
const child = plogMinLength.child({ a: 'property' })
for (var i = 0; i < max; i++) {
child.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoNodeStreamCreation (cb) {
const child = plogNodeStream.child({ a: 'property' })
for (var i = 0; i < max; i++) {
child.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoCreationWithOption (cb) {
const child = plogDest.child({ a: 'property' }, { redact: [] })
for (var i = 0; i < max; i++) {
child.info({ hello: 'world' })
}
setImmediate(cb)
}
], 10000)
run(run)
================================================
FILE: benchmarks/child.bench.js
================================================
'use strict'
const bench = require('fastbench')
const pino = require('../')
const bunyan = require('bunyan')
const bole = require('bole')('bench')('child')
const fs = require('node:fs')
const dest = fs.createWriteStream('/dev/null')
const plogNodeStream = pino(dest).child({ a: 'property' })
delete require.cache[require.resolve('../')]
const plogDest = require('../')(pino.destination('/dev/null')).child({ a: 'property' })
delete require.cache[require.resolve('../')]
const plogMinLength = require('../')(pino.destination({ dest: '/dev/null', sync: false, minLength: 4096 }))
const max = 10
const blog = bunyan.createLogger({
name: 'myapp',
streams: [{
level: 'trace',
stream: dest
}]
}).child({ a: 'property' })
require('bole').output({
level: 'info',
stream: dest
}).setFastTime(true)
const run = bench([
function benchBunyanChild (cb) {
for (var i = 0; i < max; i++) {
blog.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchBoleChild (cb) {
for (var i = 0; i < max; i++) {
bole.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoChild (cb) {
for (var i = 0; i < max; i++) {
plogDest.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoMinLengthChild (cb) {
for (var i = 0; i < max; i++) {
plogMinLength.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoNodeStreamChild (cb) {
for (var i = 0; i < max; i++) {
plogNodeStream.info({ hello: 'world' })
}
setImmediate(cb)
}
], 10000)
run(run)
================================================
FILE: benchmarks/deep-object.bench.js
================================================
'use strict'
const bench = require('fastbench')
const pino = require('../')
const bunyan = require('bunyan')
const bole = require('bole')('bench')
const winston = require('winston')
const fs = require('node:fs')
const dest = fs.createWriteStream('/dev/null')
const plogNodeStream = pino(dest)
delete require.cache[require.resolve('../')]
const plogDest = require('../')(pino.destination('/dev/null'))
delete require.cache[require.resolve('../')]
const plogMinLength = require('../')(pino.destination({ dest: '/dev/null', sync: false, minLength: 4096 }))
delete require.cache[require.resolve('../')]
const loglevel = require('./utils/wrap-log-level')(dest)
const deep = Object.assign({}, require('../package.json'), { level: 'info' })
const max = 10
const blog = bunyan.createLogger({
name: 'myapp',
streams: [{
level: 'trace',
stream: dest
}]
})
require('bole').output({
level: 'info',
stream: dest
}).setFastTime(true)
const chill = winston.createLogger({
transports: [
new winston.transports.Stream({
stream: fs.createWriteStream('/dev/null')
})
]
})
const run = bench([
function benchBunyanDeepObj (cb) {
for (var i = 0; i < max; i++) {
blog.info(deep)
}
setImmediate(cb)
},
function benchWinstonDeepObj (cb) {
for (var i = 0; i < max; i++) {
chill.log(deep)
}
setImmediate(cb)
},
function benchBoleDeepObj (cb) {
for (var i = 0; i < max; i++) {
bole.info(deep)
}
setImmediate(cb)
},
function benchLogLevelDeepObj (cb) {
for (var i = 0; i < max; i++) {
loglevel.info(deep)
}
setImmediate(cb)
},
function benchPinoDeepObj (cb) {
for (var i = 0; i < max; i++) {
plogDest.info(deep)
}
setImmediate(cb)
},
function benchPinoMinLengthDeepObj (cb) {
for (var i = 0; i < max; i++) {
plogMinLength.info(deep)
}
setImmediate(cb)
},
function benchPinoNodeStreamDeepObj (cb) {
for (var i = 0; i < max; i++) {
plogNodeStream.info(deep)
}
setImmediate(cb)
}
], 10000)
run(run)
================================================
FILE: benchmarks/formatters.bench.js
================================================
'use strict'
const formatters = {
level (label, number) {
return {
log: {
level: label
}
}
},
bindings (bindings) {
return {
process: {
pid: bindings.pid
},
host: {
name: bindings.hostname
}
}
},
log (obj) {
return { foo: 'bar', ...obj }
}
}
const bench = require('fastbench')
const pino = require('../')
delete require.cache[require.resolve('../')]
const pinoNoFormatters = require('../')(pino.destination('/dev/null'))
delete require.cache[require.resolve('../')]
const pinoFormatters = require('../')({ formatters }, pino.destination('/dev/null'))
const max = 10
const run = bench([
function benchPinoNoFormatters (cb) {
for (var i = 0; i < max; i++) {
pinoNoFormatters.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoFormatters (cb) {
for (var i = 0; i < max; i++) {
pinoFormatters.info({ hello: 'world' })
}
setImmediate(cb)
}
], 10000)
run(run)
================================================
FILE: benchmarks/internal/custom-levels.js
================================================
'use strict'
const bench = require('fastbench')
const pino = require('../../')
const base = pino(pino.destination('/dev/null'))
const baseCl = pino({
customLevels: { foo: 31 }
}, pino.destination('/dev/null'))
const child = base.child({})
const childCl = base.child({
customLevels: { foo: 31 }
})
const childOfBaseCl = baseCl.child({})
const max = 100
const run = bench([
function benchPinoNoCustomLevel (cb) {
for (var i = 0; i < max; i++) {
base.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoCustomLevel (cb) {
for (var i = 0; i < max; i++) {
baseCl.foo({ hello: 'world' })
}
setImmediate(cb)
},
function benchChildNoCustomLevel (cb) {
for (var i = 0; i < max; i++) {
child.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoChildCustomLevel (cb) {
for (var i = 0; i < max; i++) {
childCl.foo({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoChildInheritedCustomLevel (cb) {
for (var i = 0; i < max; i++) {
childOfBaseCl.foo({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoChildCreation (cb) {
const child = base.child({})
for (var i = 0; i < max; i++) {
child.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoChildCreationCustomLevel (cb) {
const child = base.child({
customLevels: { foo: 31 }
})
for (var i = 0; i < max; i++) {
child.foo({ hello: 'world' })
}
setImmediate(cb)
}
], 10000)
run(run)
================================================
FILE: benchmarks/internal/just-pino-heavy.bench.js
================================================
'use strict'
const bench = require('fastbench')
const pino = require('../../')
const fs = require('node:fs')
const dest = fs.createWriteStream('/dev/null')
const plog = pino(dest)
delete require.cache[require.resolve('../../')]
const plogDest = require('../../')(pino.destination('/dev/null'))
delete require.cache[require.resolve('../../')]
const plogAsync = require('../../')(pino.destination({ dest: '/dev/null', sync: false }))
const deep = require('../../package.json')
deep.deep = JSON.parse(JSON.stringify(deep))
deep.deep.deep = JSON.parse(JSON.stringify(deep))
const longStr = JSON.stringify(deep)
const max = 10
const run = bench([
function benchPinoLongString (cb) {
for (var i = 0; i < max; i++) {
plog.info(longStr)
}
setImmediate(cb)
},
function benchPinoDestLongString (cb) {
for (var i = 0; i < max; i++) {
plogDest.info(longStr)
}
setImmediate(cb)
},
function benchPinoAsyncLongString (cb) {
for (var i = 0; i < max; i++) {
plogAsync.info(longStr)
}
setImmediate(cb)
},
function benchPinoDeepObj (cb) {
for (var i = 0; i < max; i++) {
plog.info(deep)
}
setImmediate(cb)
},
function benchPinoDestDeepObj (cb) {
for (var i = 0; i < max; i++) {
plogDest.info(deep)
}
setImmediate(cb)
},
function benchPinoAsyncDeepObj (cb) {
for (var i = 0; i < max; i++) {
plogAsync.info(deep)
}
setImmediate(cb)
},
function benchPinoInterpolateDeep (cb) {
for (var i = 0; i < max; i++) {
plog.info('hello %j', deep)
}
setImmediate(cb)
},
function benchPinoDestInterpolateDeep (cb) {
for (var i = 0; i < max; i++) {
plogDest.info('hello %j', deep)
}
setImmediate(cb)
},
function benchPinoAsyncInterpolateDeep (cb) {
for (var i = 0; i < max; i++) {
plogAsync.info('hello %j', deep)
}
setImmediate(cb)
}
], 1000)
run(run)
================================================
FILE: benchmarks/internal/just-pino.bench.js
================================================
'use strict'
const bench = require('fastbench')
const pino = require('../../')
const fs = require('node:fs')
const dest = fs.createWriteStream('/dev/null')
const plog = pino(dest)
delete require.cache[require.resolve('../../')]
const plogDest = require('../../')(pino.destination('/dev/null'))
delete require.cache[require.resolve('../../')]
const plogAsync = require('../../')(pino.destination({ dest: '/dev/null', sync: false }))
const plogChild = plog.child({ a: 'property' })
const plogDestChild = plogDest.child({ a: 'property' })
const plogAsyncChild = plogAsync.child({ a: 'property' })
const plogChildChild = plog.child({ a: 'property' }).child({ sub: 'child' })
const plogDestChildChild = plogDest.child({ a: 'property' }).child({ sub: 'child' })
const plogAsyncChildChild = plogAsync.child({ a: 'property' }).child({ sub: 'child' })
const max = 10
const run = bench([
function benchPino (cb) {
for (var i = 0; i < max; i++) {
plog.info('hello world')
}
setImmediate(cb)
},
function benchPinoDest (cb) {
for (var i = 0; i < max; i++) {
plogDest.info('hello world')
}
setImmediate(cb)
},
function benchPinoExtreme (cb) {
for (var i = 0; i < max; i++) {
plogAsync.info('hello world')
}
setImmediate(cb)
},
function benchPinoObj (cb) {
for (var i = 0; i < max; i++) {
plog.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoDestObj (cb) {
for (var i = 0; i < max; i++) {
plogDest.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoAsyncObj (cb) {
for (var i = 0; i < max; i++) {
plogAsync.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoChild (cb) {
for (var i = 0; i < max; i++) {
plogChild.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoDestChild (cb) {
for (var i = 0; i < max; i++) {
plogDestChild.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoAsyncChild (cb) {
for (var i = 0; i < max; i++) {
plogAsyncChild.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoChildChild (cb) {
for (var i = 0; i < max; i++) {
plogChildChild.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoDestChildChild (cb) {
for (var i = 0; i < max; i++) {
plogDestChildChild.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoAsyncChildChild (cb) {
for (var i = 0; i < max; i++) {
plogAsyncChildChild.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoChildCreation (cb) {
const child = plog.child({ a: 'property' })
for (var i = 0; i < max; i++) {
child.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoDestChildCreation (cb) {
const child = plogDest.child({ a: 'property' })
for (var i = 0; i < max; i++) {
child.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoMulti (cb) {
for (var i = 0; i < max; i++) {
plog.info('hello', 'world')
}
setImmediate(cb)
},
function benchPinoDestMulti (cb) {
for (var i = 0; i < max; i++) {
plogDest.info('hello', 'world')
}
setImmediate(cb)
},
function benchPinoAsyncMulti (cb) {
for (var i = 0; i < max; i++) {
plogAsync.info('hello', 'world')
}
setImmediate(cb)
},
function benchPinoInterpolate (cb) {
for (var i = 0; i < max; i++) {
plog.info('hello %s', 'world')
}
setImmediate(cb)
},
function benchPinoDestInterpolate (cb) {
for (var i = 0; i < max; i++) {
plogDest.info('hello %s', 'world')
}
setImmediate(cb)
},
function benchPinoDestInterpolate (cb) {
for (var i = 0; i < max; i++) {
plogDest.info('hello %s', 'world')
}
setImmediate(cb)
},
function benchPinoInterpolateAll (cb) {
for (var i = 0; i < max; i++) {
plog.info('hello %s %j %d', 'world', { obj: true }, 4)
}
setImmediate(cb)
},
function benchPinoDestInterpolateAll (cb) {
for (var i = 0; i < max; i++) {
plogDest.info('hello %s %j %d', 'world', { obj: true }, 4)
}
setImmediate(cb)
},
function benchPinoAsyncInterpolateAll (cb) {
for (var i = 0; i < max; i++) {
plogAsync.info('hello %s %j %d', 'world', { obj: true }, 4)
}
setImmediate(cb)
},
function benchPinoInterpolateExtra (cb) {
for (var i = 0; i < max; i++) {
plog.info('hello %s %j %d', 'world', { obj: true }, 4, { another: 'obj' })
}
setImmediate(cb)
},
function benchPinoDestInterpolateExtra (cb) {
for (var i = 0; i < max; i++) {
plogDest.info('hello %s %j %d', 'world', { obj: true }, 4, { another: 'obj' })
}
setImmediate(cb)
},
function benchPinoAsyncInterpolateExtra (cb) {
for (var i = 0; i < max; i++) {
plogAsync.info('hello %s %j %d', 'world', { obj: true }, 4, { another: 'obj' })
}
setImmediate(cb)
}
], 10000)
run(run)
================================================
FILE: benchmarks/internal/parent-vs-child.bench.js
================================================
'use strict'
const bench = require('fastbench')
const pino = require('../../')
const base = pino(pino.destination('/dev/null'))
const child = base.child({})
const childChild = child.child({})
const childChildChild = childChild.child({})
const childChildChildChild = childChildChild.child({})
const child2 = base.child({})
const baseSerializers = pino(pino.destination('/dev/null'))
const baseSerializersChild = baseSerializers.child({})
const baseSerializersChildSerializers = baseSerializers.child({})
const max = 100
const run = bench([
function benchPinoBase (cb) {
for (var i = 0; i < max; i++) {
base.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoChild (cb) {
for (var i = 0; i < max; i++) {
child.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoChildChild (cb) {
for (var i = 0; i < max; i++) {
childChild.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoChildChildChild (cb) {
for (var i = 0; i < max; i++) {
childChildChild.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoChildChildChildChild (cb) {
for (var i = 0; i < max; i++) {
childChildChildChild.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoChild2 (cb) {
for (var i = 0; i < max; i++) {
child2.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoBaseSerializers (cb) {
for (var i = 0; i < max; i++) {
baseSerializers.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoBaseSerializersChild (cb) {
for (var i = 0; i < max; i++) {
baseSerializersChild.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoBaseSerializersChildSerializers (cb) {
for (var i = 0; i < max; i++) {
baseSerializersChildSerializers.info({ hello: 'world' })
}
setImmediate(cb)
}
], 10000)
run(run)
================================================
FILE: benchmarks/internal/redact.bench.js
================================================
'use strict'
const bench = require('fastbench')
const pino = require('../../')
const fs = require('node:fs')
const dest = fs.createWriteStream('/dev/null')
const plog = pino(dest)
delete require.cache[require.resolve('../../')]
const plogAsync = require('../../')(pino.destination({ dest: '/dev/null', sync: false }))
delete require.cache[require.resolve('../../')]
const plogUnsafe = require('../../')({ safe: false }, dest)
delete require.cache[require.resolve('../../')]
const plogUnsafeAsync = require('../../')(
{ safe: false },
pino.destination({ dest: '/dev/null', sync: false })
)
const plogRedact = pino({ redact: ['a.b.c'] }, dest)
delete require.cache[require.resolve('../../')]
const plogAsyncRedact = require('../../')(
{ redact: ['a.b.c'] },
pino.destination({ dest: '/dev/null', sync: false })
)
delete require.cache[require.resolve('../../')]
const plogUnsafeRedact = require('../../')({ redact: ['a.b.c'], safe: false }, dest)
delete require.cache[require.resolve('../../')]
const plogUnsafeAsyncRedact = require('../../')(
{ redact: ['a.b.c'], safe: false },
pino.destination({ dest: '/dev/null', sync: false })
)
const max = 10
// note that "redact me." is the same amount of bytes as the censor: "[Redacted]"
const run = bench([
function benchPinoNoRedact (cb) {
for (var i = 0; i < max; i++) {
plog.info({ a: { b: { c: 'redact me.', d: 'leave me' } } })
}
setImmediate(cb)
},
function benchPinoRedact (cb) {
for (var i = 0; i < max; i++) {
plogRedact.info({ a: { b: { c: 'redact me.', d: 'leave me' } } })
}
setImmediate(cb)
},
function benchPinoUnsafeNoRedact (cb) {
for (var i = 0; i < max; i++) {
plogUnsafe.info({ a: { b: { c: 'redact me.', d: 'leave me' } } })
}
setImmediate(cb)
},
function benchPinoUnsafeRedact (cb) {
for (var i = 0; i < max; i++) {
plogUnsafeRedact.info({ a: { b: { c: 'redact me.', d: 'leave me' } } })
}
setImmediate(cb)
},
function benchPinoAsyncNoRedact (cb) {
for (var i = 0; i < max; i++) {
plogAsync.info({ a: { b: { c: 'redact me.', d: 'leave me' } } })
}
setImmediate(cb)
},
function benchPinoAsyncRedact (cb) {
for (var i = 0; i < max; i++) {
plogAsyncRedact.info({ a: { b: { c: 'redact me.', d: 'leave me' } } })
}
setImmediate(cb)
},
function benchPinoUnsafeAsyncNoRedact (cb) {
for (var i = 0; i < max; i++) {
plogUnsafeAsync.info({ a: { b: { c: 'redact me.', d: 'leave me' } } })
}
setImmediate(cb)
},
function benchPinoUnsafeAsyncRedact (cb) {
for (var i = 0; i < max; i++) {
plogUnsafeAsyncRedact.info({ a: { b: { c: 'redact me.', d: 'leave me' } } })
}
setImmediate(cb)
}
], 10000)
run(run)
================================================
FILE: benchmarks/long-string.bench.js
================================================
'use strict'
const bench = require('fastbench')
const pino = require('../')
const bunyan = require('bunyan')
const bole = require('bole')('bench')
const winston = require('winston')
const fs = require('node:fs')
const dest = fs.createWriteStream('/dev/null')
const plogNodeStream = pino(dest)
delete require.cache[require.resolve('../')]
const plogDest = require('../')(pino.destination('/dev/null'))
delete require.cache[require.resolve('../')]
const plogMinLength = require('../')(pino.destination({ dest: '/dev/null', sync: false, minLength: 4096 }))
const crypto = require('crypto')
const longStr = crypto.randomBytes(2000).toString()
const max = 10
const blog = bunyan.createLogger({
name: 'myapp',
streams: [{
level: 'trace',
stream: dest
}]
})
require('bole').output({
level: 'info',
stream: dest
}).setFastTime(true)
const chill = winston.createLogger({
transports: [
new winston.transports.Stream({
stream: fs.createWriteStream('/dev/null')
})
]
})
const run = bench([
function benchBunyan (cb) {
for (var i = 0; i < max; i++) {
blog.info(longStr)
}
setImmediate(cb)
},
function benchWinston (cb) {
for (var i = 0; i < max; i++) {
chill.info(longStr)
}
setImmediate(cb)
},
function benchBole (cb) {
for (var i = 0; i < max; i++) {
bole.info(longStr)
}
setImmediate(cb)
},
function benchPino (cb) {
for (var i = 0; i < max; i++) {
plogDest.info(longStr)
}
setImmediate(cb)
},
function benchPinoMinLength (cb) {
for (var i = 0; i < max; i++) {
plogMinLength.info(longStr)
}
setImmediate(cb)
},
function benchPinoNodeStream (cb) {
for (var i = 0; i < max; i++) {
plogNodeStream.info(longStr)
}
setImmediate(cb)
}
], 1000)
run(run)
================================================
FILE: benchmarks/multi-arg.bench.js
================================================
'use strict'
const bench = require('fastbench')
const pino = require('../')
const bunyan = require('bunyan')
const bole = require('bole')('bench')
const winston = require('winston')
const fs = require('node:fs')
const dest = fs.createWriteStream('/dev/null')
const plogNodeStream = pino(dest)
delete require.cache[require.resolve('../')]
const plogDest = require('../')(pino.destination('/dev/null'))
delete require.cache[require.resolve('../')]
const plogMinLength = require('../')(pino.destination({ dest: '/dev/null', sync: false, minLength: 4096 }))
delete require.cache[require.resolve('../')]
const deep = require('../package.json')
deep.deep = Object.assign({}, JSON.parse(JSON.stringify(deep)))
deep.deep.deep = Object.assign({}, JSON.parse(JSON.stringify(deep)))
deep.deep.deep.deep = Object.assign({}, JSON.parse(JSON.stringify(deep)))
const blog = bunyan.createLogger({
name: 'myapp',
streams: [{
level: 'trace',
stream: dest
}]
})
require('bole').output({
level: 'info',
stream: dest
}).setFastTime(true)
const chill = winston.createLogger({
transports: [
new winston.transports.Stream({
stream: fs.createWriteStream('/dev/null')
})
]
})
const max = 10
const run = bench([
function benchBunyanInterpolate (cb) {
for (var i = 0; i < max; i++) {
blog.info('hello %s', 'world')
}
setImmediate(cb)
},
function benchWinstonInterpolate (cb) {
for (var i = 0; i < max; i++) {
chill.log('info', 'hello %s', 'world')
}
setImmediate(cb)
},
function benchBoleInterpolate (cb) {
for (var i = 0; i < max; i++) {
bole.info('hello %s', 'world')
}
setImmediate(cb)
},
function benchPinoInterpolate (cb) {
for (var i = 0; i < max; i++) {
plogDest.info('hello %s', 'world')
}
setImmediate(cb)
},
function benchPinoMinLengthInterpolate (cb) {
for (var i = 0; i < max; i++) {
plogMinLength.info('hello %s', 'world')
}
setImmediate(cb)
},
function benchPinoNodeStreamInterpolate (cb) {
for (var i = 0; i < max; i++) {
plogNodeStream.info('hello %s', 'world')
}
setImmediate(cb)
},
function benchBunyanInterpolateAll (cb) {
for (var i = 0; i < max; i++) {
blog.info('hello %s %j %d', 'world', { obj: true }, 4)
}
setImmediate(cb)
},
function benchWinstonInterpolateAll (cb) {
for (var i = 0; i < max; i++) {
chill.log('info', 'hello %s %j %d', 'world', { obj: true }, 4)
}
setImmediate(cb)
},
function benchBoleInterpolateAll (cb) {
for (var i = 0; i < max; i++) {
bole.info('hello %s %j %d', 'world', { obj: true }, 4)
}
setImmediate(cb)
},
function benchPinoInterpolateAll (cb) {
for (var i = 0; i < max; i++) {
plogDest.info('hello %s %j %d', 'world', { obj: true }, 4)
}
setImmediate(cb)
},
function benchPinoMinLengthInterpolateAll (cb) {
for (var i = 0; i < max; i++) {
plogMinLength.info('hello %s %j %d', 'world', { obj: true }, 4)
}
setImmediate(cb)
},
function benchPinoNodeStreamInterpolateAll (cb) {
for (var i = 0; i < max; i++) {
plogNodeStream.info('hello %s %j %d', 'world', { obj: true }, 4)
}
setImmediate(cb)
},
function benchBunyanInterpolateExtra (cb) {
for (var i = 0; i < max; i++) {
blog.info('hello %s %j %d', 'world', { obj: true }, 4, { another: 'obj' })
}
setImmediate(cb)
},
function benchWinstonInterpolateExtra (cb) {
for (var i = 0; i < max; i++) {
chill.log('info', 'hello %s %j %d', 'world', { obj: true }, 4, { another: 'obj' })
}
setImmediate(cb)
},
function benchBoleInterpolateExtra (cb) {
for (var i = 0; i < max; i++) {
bole.info('hello %s %j %d', 'world', { obj: true }, 4, { another: 'obj' })
}
setImmediate(cb)
},
function benchPinoInterpolateExtra (cb) {
for (var i = 0; i < max; i++) {
plogDest.info('hello %s %j %d', 'world', { obj: true }, 4, { another: 'obj' })
}
setImmediate(cb)
},
function benchPinoMinLengthInterpolateExtra (cb) {
for (var i = 0; i < max; i++) {
plogMinLength.info('hello %s %j %d', 'world', { obj: true }, 4, { another: 'obj' })
}
setImmediate(cb)
},
function benchPinoNodeStreamInterpolateExtra (cb) {
for (var i = 0; i < max; i++) {
plogNodeStream.info('hello %s %j %d', 'world', { obj: true }, 4, { another: 'obj' })
}
setImmediate(cb)
},
function benchBunyanInterpolateDeep (cb) {
for (var i = 0; i < max; i++) {
blog.info('hello %j', deep)
}
setImmediate(cb)
},
function benchWinstonInterpolateDeep (cb) {
for (var i = 0; i < max; i++) {
chill.log('info', 'hello %j', deep)
}
setImmediate(cb)
},
function benchBoleInterpolateDeep (cb) {
for (var i = 0; i < max; i++) {
bole.info('hello %j', deep)
}
setImmediate(cb)
},
function benchPinoInterpolateDeep (cb) {
for (var i = 0; i < max; i++) {
plogDest.info('hello %j', deep)
}
setImmediate(cb)
},
function benchPinoMinLengthInterpolateDeep (cb) {
for (var i = 0; i < max; i++) {
plogMinLength.info('hello %j', deep)
}
setImmediate(cb)
},
function benchPinoNodeStreamInterpolateDeep (cb) {
for (var i = 0; i < max; i++) {
plogNodeStream.info('hello %j', deep)
}
setImmediate(cb)
}
], 10000)
run(run)
================================================
FILE: benchmarks/multistream.js
================================================
'use strict'
const bench = require('fastbench')
const bunyan = require('bunyan')
const pino = require('../')
const fs = require('node:fs')
const dest = fs.createWriteStream('/dev/null')
const tenStreams = [
{ stream: dest },
{ stream: dest },
{ stream: dest },
{ stream: dest },
{ stream: dest },
{ level: 'debug', stream: dest },
{ level: 'debug', stream: dest },
{ level: 'trace', stream: dest },
{ level: 'warn', stream: dest },
{ level: 'fatal', stream: dest }
]
const pinomsTen = pino({ level: 'debug' }, pino.multistream(tenStreams))
const fourStreams = [
{ stream: dest },
{ stream: dest },
{ level: 'debug', stream: dest },
{ level: 'trace', stream: dest }
]
const pinomsFour = pino({ level: 'debug' }, pino.multistream(fourStreams))
const pinomsOne = pino({ level: 'info' }, pino.multistream(dest))
const blogOne = bunyan.createLogger({
name: 'myapp',
streams: [{ stream: dest }]
})
const blogTen = bunyan.createLogger({
name: 'myapp',
streams: tenStreams
})
const blogFour = bunyan.createLogger({
name: 'myapp',
streams: fourStreams
})
const max = 10
const run = bench([
function benchBunyanTen (cb) {
for (let i = 0; i < max; i++) {
blogTen.info('hello world')
blogTen.debug('hello world')
blogTen.trace('hello world')
blogTen.warn('hello world')
blogTen.fatal('hello world')
}
setImmediate(cb)
},
function benchPinoMSTen (cb) {
for (let i = 0; i < max; i++) {
pinomsTen.info('hello world')
pinomsTen.debug('hello world')
pinomsTen.trace('hello world')
pinomsTen.warn('hello world')
pinomsTen.fatal('hello world')
}
setImmediate(cb)
},
function benchBunyanFour (cb) {
for (let i = 0; i < max; i++) {
blogFour.info('hello world')
blogFour.debug('hello world')
blogFour.trace('hello world')
}
setImmediate(cb)
},
function benchPinoMSFour (cb) {
for (let i = 0; i < max; i++) {
pinomsFour.info('hello world')
pinomsFour.debug('hello world')
pinomsFour.trace('hello world')
}
setImmediate(cb)
},
function benchBunyanOne (cb) {
for (let i = 0; i < max; i++) {
blogOne.info('hello world')
}
setImmediate(cb)
},
function benchPinoMSOne (cb) {
for (let i = 0; i < max; i++) {
pinomsOne.info('hello world')
}
setImmediate(cb)
}
], 10000)
run()
================================================
FILE: benchmarks/object.bench.js
================================================
'use strict'
const bench = require('fastbench')
const pino = require('../')
const bunyan = require('bunyan')
const bole = require('bole')('bench')
const winston = require('winston')
const fs = require('node:fs')
const dest = fs.createWriteStream('/dev/null')
const loglevel = require('./utils/wrap-log-level')(dest)
const plogNodeStream = pino(dest)
delete require.cache[require.resolve('../')]
const plogDest = require('../')(pino.destination('/dev/null'))
delete require.cache[require.resolve('../')]
const plogMinLength = require('../')(pino.destination({ dest: '/dev/null', sync: false, minLength: 4096 }))
const blog = bunyan.createLogger({
name: 'myapp',
streams: [{
level: 'trace',
stream: dest
}]
})
require('bole').output({
level: 'info',
stream: dest
}).setFastTime(true)
const chill = winston.createLogger({
transports: [
new winston.transports.Stream({
stream: fs.createWriteStream('/dev/null')
})
]
})
const max = 10
const run = bench([
function benchBunyanObj (cb) {
for (var i = 0; i < max; i++) {
blog.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchWinstonObj (cb) {
for (var i = 0; i < max; i++) {
chill.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchBoleObj (cb) {
for (var i = 0; i < max; i++) {
bole.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchLogLevelObject (cb) {
for (var i = 0; i < max; i++) {
loglevel.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoObj (cb) {
for (var i = 0; i < max; i++) {
plogDest.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoMinLengthObj (cb) {
for (var i = 0; i < max; i++) {
plogMinLength.info({ hello: 'world' })
}
setImmediate(cb)
},
function benchPinoNodeStreamObj (cb) {
for (var i = 0; i < max; i++) {
plogNodeStream.info({ hello: 'world' })
}
setImmediate(cb)
}
], 10000)
run(run)
================================================
FILE: benchmarks/utils/generate-benchmark-doc.js
================================================
'use strict'
const { join } = require('node:path')
const { execSync } = require('node:child_process')
const run = (type) => {
process.stderr.write(`benchmarking ${type}\n`)
return execSync(`node ${join(__dirname, 'runbench')} ${type} -q`)
}
console.log(`
# Benchmarks
\`pino.info('hello world')\`:
\`\`\`
${run('basic')}
\`\`\`
\`pino.info({'hello': 'world'})\`:
\`\`\`
${run('object')}
\`\`\`
\`pino.info(aBigDeeplyNestedObject)\`:
\`\`\`
${run('deep-object')}
\`\`\`
\`pino.info('hello %s %j %d', 'world', {obj: true}, 4, {another: 'obj'})\`:
For a fair comparison, [LogLevel](http://npm.im/loglevel) was extended
to include a timestamp and [bole](http://npm.im/bole) had
\`fastTime\` mode switched on.
`)
================================================
FILE: benchmarks/utils/runbench.js
================================================
'use strict'
const { type, platform, arch, release, cpus } = require('node:os')
const { resolve, join } = require('node:path')
const spawn = require('node:child_process').spawn
const pump = require('pump')
const split = require('split2')
const through = require('through2')
const steed = require('steed')
function usage () {
console.log(`
Pino Benchmarks
To run a benchmark, specify which to run:
・all ⁃ run all benchmarks (takes a while)
・basic ⁃ log a simple string
・object ⁃ logging a basic object
・deep-object ⁃ logging a large object
・multi-arg ⁃ multiple log method arguments
・child ⁃ child from a parent
・child-child ⁃ child from a child
・child-creation ⁃ child constructor
・formatters ⁃ difference between with or without formatters
Example:
node runbench basic
`)
}
if (!process.argv[2]) {
usage()
process.exit()
}
const quiet = process.argv[3] === '-q'
const selectedBenchmark = process.argv[2].toLowerCase()
const benchmarkDir = resolve(__dirname, '..')
const benchmarks = {
basic: 'basic.bench.js',
object: 'object.bench.js',
'deep-object': 'deep-object.bench.js',
'multi-arg': 'multi-arg.bench.js',
'long-string': 'long-string.bench.js',
child: 'child.bench.js',
'child-child': 'child-child.bench.js',
'child-creation': 'child-creation.bench.js',
formatters: 'formatters.bench.js'
}
function runBenchmark (name, done) {
const benchmarkResults = {}
benchmarkResults[name] = {}
const processor = through(function (line, enc, cb) {
const [label, time] = ('' + line).split(': ')
const [target, iterations] = label.split('*')
const logger = target.replace('bench', '')
if (!benchmarkResults[name][logger]) benchmarkResults[name][logger] = []
benchmarkResults[name][logger].push({
time: time.replace('ms', ''),
iterations: iterations.replace(':', '')
})
cb()
})
if (quiet === false) console.log(`Running ${name.toUpperCase()} benchmark\n`)
const benchmark = spawn(
process.argv[0],
[join(benchmarkDir, benchmarks[name])]
)
if (quiet === false) {
benchmark.stdout.pipe(process.stdout)
}
pump(benchmark.stdout, split(), processor)
benchmark.on('exit', () => {
console.log()
if (done && typeof done === 'function') done(null, benchmarkResults)
})
}
function sum (arr) {
let result = 0
for (var i = 0; i < arr.length; i += 1) {
result += Number.parseFloat(arr[i].time)
}
return result
}
function displayResults (results) {
if (quiet === false) console.log('==========')
const benchNames = Object.keys(results)
for (var i = 0; i < benchNames.length; i += 1) {
console.log(`${benchNames[i].toUpperCase()} benchmark averages`)
const benchmark = results[benchNames[i]]
const loggers = Object.keys(benchmark)
for (var j = 0; j < loggers.length; j += 1) {
const logger = benchmark[loggers[j]]
const average = sum(logger) / logger.length
console.log(`${loggers[j]} average: ${average.toFixed(3)}ms`)
}
}
if (quiet === false) {
console.log('==========')
console.log(
`System: ${type()}/${platform()} ${arch()} ${release()}`,
`~ ${cpus()[0].model} (cores/threads: ${cpus().length})`
)
}
}
function toBench (done) {
runBenchmark(this.name, done)
}
const benchQueue = []
if (selectedBenchmark !== 'all') {
benchQueue.push(toBench.bind({ name: selectedBenchmark }))
} else {
const keys = Object.keys(benchmarks)
for (var i = 0; i < keys.length; i += 1) {
benchQueue.push(toBench.bind({ name: keys[i] }))
}
}
steed.series(benchQueue, function (err, results) {
if (err) return console.error(err.message)
results.forEach(displayResults)
})
================================================
FILE: benchmarks/utils/wrap-log-level.js
================================================
'use strict'
const { readFileSync } = require('node:fs')
const vm = require('vm')
const { join } = require('node:path')
const code = readFileSync(
join(__dirname, '..', '..', 'node_modules', 'loglevel', 'lib', 'loglevel.js')
)
const { Console } = require('console')
function build (dest) {
const sandbox = {
module: {},
console: new Console(dest, dest)
}
const context = vm.createContext(sandbox)
const script = new vm.Script(code)
script.runInContext(context)
const loglevel = sandbox.log
const originalFactory = loglevel.methodFactory
loglevel.methodFactory = function (methodName, logLevel, loggerName) {
const rawMethod = originalFactory(methodName, logLevel, loggerName)
return function () {
const time = new Date()
let array
if (typeof arguments[0] === 'string') {
arguments[0] = '[' + time.toISOString() + '] ' + arguments[0]
rawMethod.apply(null, arguments)
} else {
array = new Array(arguments.length + 1)
array[0] = '[' + time.toISOString() + ']'
for (var i = 0; i < arguments.length; i++) {
array[i + 1] = arguments[i]
}
rawMethod.apply(null, array)
}
}
}
loglevel.setLevel(loglevel.levels.INFO)
return loglevel
}
module.exports = build
if (require.main === module) {
const loglevel = build(process.stdout)
loglevel.info('hello')
loglevel.info({ hello: 'world' })
loglevel.info('hello %j', { hello: 'world' })
}
================================================
FILE: bin.js
================================================
#!/usr/bin/env node
console.error(
'`pino` cli has been removed. Use `pino-pretty` cli instead.\n' +
'\nSee: https://github.com/pinojs/pino-pretty'
)
process.exit(1)
================================================
FILE: browser.js
================================================
'use strict'
const format = require('quick-format-unescaped')
const Redact = require('@pinojs/redact')
module.exports = pino
const CENSOR = '[Redacted]'
function handleRedactOpts (opts) {
if (Array.isArray(opts)) {
return { paths: opts, censor: CENSOR }
}
const { paths, censor = CENSOR, remove } = opts
if (!Array.isArray(paths)) {
throw Error('pino – redact must contain an array of strings')
}
return { paths, censor: remove ? undefined : censor, remove }
}
function buildRedactFn (redact) {
if (!redact) return null
const { paths, censor, remove } = handleRedactOpts(redact)
return Redact({
paths,
censor,
remove,
serialize: false
})
}
const _console = pfGlobalThisOrFallback().console || {}
const stdSerializers = {
mapHttpRequest: mock,
mapHttpResponse: mock,
wrapRequestSerializer: passthrough,
wrapResponseSerializer: passthrough,
wrapErrorSerializer: passthrough,
req: mock,
res: mock,
err: asErrValue,
errWithCause: asErrValue
}
function levelToValue (level, logger) {
return level === 'silent'
? Infinity
: logger.levels.values[level]
}
const baseLogFunctionSymbol = Symbol('pino.logFuncs')
const hierarchySymbol = Symbol('pino.hierarchy')
const logFallbackMap = {
error: 'log',
fatal: 'error',
warn: 'error',
info: 'log',
debug: 'log',
trace: 'log'
}
function appendChildLogger (parentLogger, childLogger) {
const newEntry = {
logger: childLogger,
parent: parentLogger[hierarchySymbol]
}
childLogger[hierarchySymbol] = newEntry
}
function setupBaseLogFunctions (logger, levels, proto) {
const logFunctions = {}
levels.forEach(level => {
logFunctions[level] = proto[level] ? proto[level] : (_console[level] || _console[logFallbackMap[level] || 'log'] || noop)
})
logger[baseLogFunctionSymbol] = logFunctions
}
function shouldSerialize (serialize, serializers) {
if (Array.isArray(serialize)) {
const hasToFilter = serialize.filter(function (k) {
return k !== '!stdSerializers.err'
})
return hasToFilter
} else if (serialize === true) {
return Object.keys(serializers)
}
return false
}
function pino (opts) {
opts = opts || {}
opts.browser = opts.browser || {}
const transmit = opts.browser.transmit
if (transmit && typeof transmit.send !== 'function') { throw Error('pino: transmit option must have a send function') }
const proto = opts.browser.write || _console
if (opts.browser.write) opts.browser.asObject = true
const serializers = opts.serializers || {}
const serialize = shouldSerialize(opts.browser.serialize, serializers)
let stdErrSerialize = opts.browser.serialize
if (
Array.isArray(opts.browser.serialize) &&
opts.browser.serialize.indexOf('!stdSerializers.err') > -1
) stdErrSerialize = false
const customLevels = Object.keys(opts.customLevels || {})
const levels = ['error', 'fatal', 'warn', 'info', 'debug', 'trace'].concat(customLevels)
if (typeof proto === 'function') {
levels.forEach(function (level) {
proto[level] = proto
})
}
if (opts.enabled === false || opts.browser.disabled) opts.level = 'silent'
const level = opts.level || 'info'
const logger = Object.create(proto)
if (!logger.log) logger.log = noop
setupBaseLogFunctions(logger, levels, proto)
// setup root hierarchy entry
appendChildLogger({}, logger)
Object.defineProperty(logger, 'levelVal', {
get: getLevelVal
})
Object.defineProperty(logger, 'level', {
get: getLevel,
set: setLevel
})
const redactFn = buildRedactFn(opts.redact)
const setOpts = {
transmit,
serialize,
asObject: opts.browser.asObject,
asObjectBindingsOnly: opts.browser.asObjectBindingsOnly,
formatters: opts.browser.formatters,
reportCaller: opts.browser.reportCaller,
levels,
timestamp: getTimeFunction(opts),
messageKey: opts.messageKey || 'msg',
onChild: opts.onChild || noop,
redactFn
}
logger.levels = getLevels(opts)
logger.level = level
logger.isLevelEnabled = function (level) {
if (!this.levels.values[level]) {
return false
}
return this.levels.values[level] >= this.levels.values[this.level]
}
logger.setMaxListeners = logger.getMaxListeners =
logger.emit = logger.addListener = logger.on =
logger.prependListener = logger.once =
logger.prependOnceListener = logger.removeListener =
logger.removeAllListeners = logger.listeners =
logger.listenerCount = logger.eventNames =
logger.write = logger.flush = noop
logger.serializers = serializers
logger._serialize = serialize
logger._stdErrSerialize = stdErrSerialize
logger.child = function (...args) { return child.call(this, setOpts, ...args) }
if (transmit) logger._logEvent = createLogEventShape()
function getLevelVal () {
return levelToValue(this.level, this)
}
function getLevel () {
return this._level
}
function setLevel (level) {
if (level !== 'silent' && !this.levels.values[level]) {
throw Error('unknown level ' + level)
}
this._level = level
set(this, setOpts, logger, 'error') // <-- must stay first
set(this, setOpts, logger, 'fatal')
set(this, setOpts, logger, 'warn')
set(this, setOpts, logger, 'info')
set(this, setOpts, logger, 'debug')
set(this, setOpts, logger, 'trace')
customLevels.forEach((level) => {
set(this, setOpts, logger, level)
})
}
function child (setOpts, bindings, childOptions) {
if (!bindings) {
throw new Error('missing bindings for child Pino')
}
childOptions = childOptions || {}
if (serialize && bindings.serializers) {
childOptions.serializers = bindings.serializers
}
const childOptionsSerializers = childOptions.serializers
if (serialize && childOptionsSerializers) {
var childSerializers = Object.assign({}, serializers, childOptionsSerializers)
var childSerialize = opts.browser.serialize === true
? Object.keys(childSerializers)
: serialize
delete bindings.serializers
applySerializers([bindings], childSerialize, childSerializers, this._stdErrSerialize)
}
function Child (parent) {
this._childLevel = (parent._childLevel | 0) + 1
// make sure bindings are available in the `set` function
this.bindings = bindings
if (childSerializers) {
this.serializers = childSerializers
this._serialize = childSerialize
}
if (transmit) {
this._logEvent = createLogEventShape(
[].concat(parent._logEvent.bindings, bindings)
)
}
}
Child.prototype = this
const newLogger = new Child(this)
// must happen before the level is assigned
appendChildLogger(this, newLogger)
newLogger.child = function (...args) { return child.call(this, setOpts, ...args) }
// required to actually initialize the logger functions for any given child
newLogger.level = childOptions.level || this.level // allow level to be set by childOptions
setOpts.onChild(newLogger)
return newLogger
}
return logger
}
function getLevels (opts) {
const customLevels = opts.customLevels || {}
const values = Object.assign({}, pino.levels.values, customLevels)
const labels = Object.assign({}, pino.levels.labels, invertObject(customLevels))
return {
values,
labels
}
}
function invertObject (obj) {
const inverted = {}
Object.keys(obj).forEach(function (key) {
inverted[obj[key]] = key
})
return inverted
}
pino.levels = {
values: {
fatal: 60,
error: 50,
warn: 40,
info: 30,
debug: 20,
trace: 10
},
labels: {
10: 'trace',
20: 'debug',
30: 'info',
40: 'warn',
50: 'error',
60: 'fatal'
}
}
pino.stdSerializers = stdSerializers
pino.stdTimeFunctions = Object.assign({}, { nullTime, epochTime, unixTime, isoTime })
function getBindingChain (logger) {
const bindings = []
if (logger.bindings) {
bindings.push(logger.bindings)
}
// traverse up the tree to get all bindings
let hierarchy = logger[hierarchySymbol]
while (hierarchy.parent) {
hierarchy = hierarchy.parent
if (hierarchy.logger.bindings) {
bindings.push(hierarchy.logger.bindings)
}
}
return bindings.reverse()
}
function set (self, opts, rootLogger, level) {
// override the current log functions with either `noop` or the base log function
Object.defineProperty(self, level, {
value: (levelToValue(self.level, rootLogger) > levelToValue(level, rootLogger)
? noop
: rootLogger[baseLogFunctionSymbol][level]),
writable: true,
enumerable: true,
configurable: true
})
if (self[level] === noop) {
if (!opts.transmit) return
const transmitLevel = opts.transmit.level || self.level
const transmitValue = levelToValue(transmitLevel, rootLogger)
const methodValue = levelToValue(level, rootLogger)
if (methodValue < transmitValue) return
}
// make sure the log format is correct
self[level] = createWrap(self, opts, rootLogger, level)
// prepend bindings if it is not the root logger
const bindings = getBindingChain(self)
if (bindings.length === 0) {
// early exit in case for rootLogger
return
}
self[level] = prependBindingsInArguments(bindings, self[level])
}
function prependBindingsInArguments (bindings, logFunc) {
return function () {
return logFunc.apply(this, [...bindings, ...arguments])
}
}
function createWrap (self, opts, rootLogger, level) {
return (function (write) {
return function LOG () {
const ts = opts.timestamp()
const args = new Array(arguments.length)
const proto = (Object.getPrototypeOf && Object.getPrototypeOf(this) === _console) ? _console : this
for (var i = 0; i < args.length; i++) args[i] = arguments[i]
var argsIsSerialized = false
if (opts.serialize) {
applySerializers(args, this._serialize, this.serializers, this._stdErrSerialize)
argsIsSerialized = true
}
if (opts.asObject || opts.formatters) {
const out = asObject(this, level, args, ts, opts)
if (opts.reportCaller && out && out.length > 0 && out[0] && typeof out[0] === 'object') {
try {
const caller = getCallerLocation()
if (caller) out[0].caller = caller
} catch (e) {}
}
write.call(proto, ...out)
} else {
if (opts.reportCaller) {
try {
const caller = getCallerLocation()
if (caller) args.push(caller)
} catch (e) {}
}
write.apply(proto, args)
}
if (opts.transmit) {
const transmitLevel = opts.transmit.level || self._level
const transmitValue = levelToValue(transmitLevel, rootLogger)
const methodValue = levelToValue(level, rootLogger)
if (methodValue < transmitValue) return
transmit(this, {
ts,
methodLevel: level,
methodValue,
transmitLevel,
transmitValue: rootLogger.levels.values[opts.transmit.level || self._level],
send: opts.transmit.send,
val: levelToValue(self._level, rootLogger)
}, args, argsIsSerialized)
}
}
})(self[baseLogFunctionSymbol][level])
}
function asObject (logger, level, args, ts, opts) {
const {
level: levelFormatter,
log: logObjectFormatter = (obj) => obj
} = opts.formatters || {}
const argsCloned = args.slice()
let msg = argsCloned[0]
const logObject = {}
let lvl = (logger._childLevel | 0) + 1
if (lvl < 1) lvl = 1
if (ts) {
logObject.time = ts
}
if (levelFormatter) {
const formattedLevel = levelFormatter(level, logger.levels.values[level])
Object.assign(logObject, formattedLevel)
} else {
logObject.level = logger.levels.values[level]
}
if (opts.asObjectBindingsOnly) {
if (msg !== null && typeof msg === 'object') {
while (lvl-- && typeof argsCloned[0] === 'object') {
Object.assign(logObject, argsCloned.shift())
}
}
let formattedLogObject = logObjectFormatter(logObject)
if (opts.redactFn) {
formattedLogObject = opts.redactFn(formattedLogObject)
}
return [formattedLogObject, ...argsCloned]
} else {
// deliberate, catching objects, arrays
if (msg !== null && typeof msg === 'object') {
while (lvl-- && typeof argsCloned[0] === 'object') {
Object.assign(logObject, argsCloned.shift())
}
msg = argsCloned.length ? format(argsCloned.shift(), argsCloned) : undefined
} else if (typeof msg === 'string') msg = format(argsCloned.shift(), argsCloned)
if (msg !== undefined) logObject[opts.messageKey] = msg
let formattedLogObject = logObjectFormatter(logObject)
if (opts.redactFn) {
formattedLogObject = opts.redactFn(formattedLogObject)
}
return [formattedLogObject]
}
}
function applySerializers (args, serialize, serializers, stdErrSerialize) {
for (const i in args) {
if (stdErrSerialize && args[i] instanceof Error) {
args[i] = pino.stdSerializers.err(args[i])
} else if (typeof args[i] === 'object' && !Array.isArray(args[i]) && serialize) {
for (const k in args[i]) {
if (serialize.indexOf(k) > -1 && k in serializers) {
args[i][k] = serializers[k](args[i][k])
}
}
}
}
}
function transmit (logger, opts, args, argsIsSerialized = false) {
const send = opts.send
const ts = opts.ts
const methodLevel = opts.methodLevel
const methodValue = opts.methodValue
const val = opts.val
const bindings = logger._logEvent.bindings
if (!argsIsSerialized) {
applySerializers(
args,
logger._serialize || Object.keys(logger.serializers),
logger.serializers,
logger._stdErrSerialize === undefined ? true : logger._stdErrSerialize
)
}
logger._logEvent.ts = ts
logger._logEvent.messages = args.filter(function (arg) {
// bindings can only be objects, so reference equality check via indexOf is fine
return bindings.indexOf(arg) === -1
})
logger._logEvent.level.label = methodLevel
logger._logEvent.level.value = methodValue
send(methodLevel, logger._logEvent, val)
logger._logEvent = createLogEventShape(bindings)
}
function createLogEventShape (bindings) {
return {
ts: 0,
messages: [],
bindings: bindings || [],
level: { label: '', value: 0 }
}
}
function asErrValue (err) {
const obj = {
type: err.constructor.name,
msg: err.message,
stack: err.stack
}
for (const key in err) {
if (obj[key] === undefined) {
obj[key] = err[key]
}
}
return obj
}
function getTimeFunction (opts) {
if (typeof opts.timestamp === 'function') {
return opts.timestamp
}
if (opts.timestamp === false) {
return nullTime
}
return epochTime
}
function mock () { return {} }
function passthrough (a) { return a }
function noop () {}
function nullTime () { return false }
function epochTime () { return Date.now() }
function unixTime () { return Math.round(Date.now() / 1000.0) }
function isoTime () { return new Date(Date.now()).toISOString() } // using Date.now() for testability
/* eslint-disable */
/* istanbul ignore next */
function pfGlobalThisOrFallback () {
function defd (o) { return typeof o !== 'undefined' && o }
try {
if (typeof globalThis !== 'undefined') return globalThis
Object.defineProperty(Object.prototype, 'globalThis', {
get: function () {
delete Object.prototype.globalThis
return (this.globalThis = this)
},
configurable: true
})
return globalThis
} catch (e) {
return defd(self) || defd(window) || defd(this) || {}
}
}
/* eslint-enable */
module.exports.default = pino
module.exports.pino = pino
// Attempt to extract the user callsite (file:line:column)
/* istanbul ignore next */
function getCallerLocation () {
const stack = (new Error()).stack
if (!stack) return null
const lines = stack.split('\n')
for (let i = 1; i < lines.length; i++) {
const l = lines[i].trim()
// skip frames from this file and internals
if (/(^at\s+)?(createWrap|LOG|set\s*\(|asObject|Object\.apply|Function\.apply)/.test(l)) continue
if (l.indexOf('browser.js') !== -1) continue
if (l.indexOf('node:internal') !== -1) continue
if (l.indexOf('node_modules') !== -1) continue
// try formats like: at func (file:line:col) or at file:line:col
let m = l.match(/\((.*?):(\d+):(\d+)\)/)
if (!m) m = l.match(/at\s+(.*?):(\d+):(\d+)/)
if (m) {
const file = m[1]
const line = m[2]
const col = m[3]
return file + ':' + line + ':' + col
}
}
return null
}
================================================
FILE: build/sync-version.js
================================================
'use strict'
const fs = require('node:fs')
const path = require('node:path')
let { version } = require('../package.json')
let passedVersion = process.argv[2]
if (passedVersion) {
passedVersion = passedVersion.trim().replace(/^v/, '')
if (version !== passedVersion) {
console.log(`Syncing version from ${version} to ${passedVersion}`)
version = passedVersion
const packageJson = require('../package.json')
packageJson.version = version
fs.writeFileSync(path.resolve('./package.json'), JSON.stringify(packageJson, null, 2) + '\n', { encoding: 'utf-8' })
}
}
const metaContent = `'use strict'
module.exports = { version: '${version}' }
`
fs.writeFileSync(path.resolve('./lib/meta.js'), metaContent, { encoding: 'utf-8' })
================================================
FILE: docs/api.md
================================================
# API
* [pino() => logger](#export)
* [options](#options)
* [destination](#destination)
* [destination\[Symbol.for('pino.metadata')\]](#metadata)
* [Logger Instance](#logger)
* [logger.trace()](#trace)
* [logger.debug()](#debug)
* [logger.info()](#info)
* [logger.warn()](#warn)
* [logger.error()](#error)
* [logger.fatal()](#fatal)
* [logger.silent()](#silent)
* [logger.child()](#child)
* [logger.bindings()](#logger-bindings)
* [logger.setBindings()](#logger-set-bindings)
* [logger.flush()](#flush)
* [logger.level](#logger-level)
* [logger.isLevelEnabled()](#islevelenabled)
* [logger.levels](#levels)
* [logger\[Symbol.for('pino.serializers')\]](#serializers)
* [Event: 'level-change'](#level-change)
* [logger.version](#version)
* [logger.msgPrefix](#msgPrefix)
* [Statics](#statics)
* [pino.destination()](#pino-destination)
* [pino.transport()](#pino-transport)
* [pino.multistream()](#pino-multistream)
* [pino.stdSerializers](#pino-stdserializers)
* [pino.stdTimeFunctions](#pino-stdtimefunctions)
* [pino.symbols](#pino-symbols)
* [pino.version](#pino-version)
* [Interfaces](#interfaces)
* [MultiStreamRes](#multistreamres)
* [StreamEntry](#streamentry)
* [DestinationStream](#destinationstream)
* [Types](#types)
* [Level](#level-1)
* [TypeScript](#typescript)
* [Module Augmentation](#module-augmentation)
* [LogFnFields Interface](#logfnfields-interface)
<a id="export"></a>
## `pino([options], [destination]) => logger`
The exported `pino` function takes two optional arguments,
[`options`](#options) and [`destination`](#destination), and
returns a [logger instance](#logger).
<a id=options></a>
### `options` (Object)
#### `name` (String)
Default: `undefined`
The name of the logger. When set adds a `name` field to every JSON line logged.
#### `level` (String)
Default: `'info'`
The minimum level to log: Pino will not log messages with a lower level. Setting this option reduces the load, as typically, debug and trace logs are only valid for development, and not needed in production.
One of `'fatal'`, `'error'`, `'warn'`, `'info'`, `'debug'`, `'trace'` or `'silent'`.
Additional levels can be added to the instance via the `customLevels` option.
* See [`customLevels` option](#opt-customlevels)
<a id=opt-customlevels></a>
#### `levelComparison` ("ASC", "DESC", Function)
Default: `ASC`
Use this option to customize levels order.
In order to be able to define custom levels ordering pass a function which will accept `current` and `expected` values and return `boolean` which shows should `current` level to be shown or not.
```js
const logger = pino({
levelComparison: 'DESC',
customLevels: {
foo: 20, // `foo` is more valuable than `bar`
bar: 10
},
})
// OR
const logger = pino({
levelComparison: function(current, expected) {
return current >= expected;
}
})
```
#### `customLevels` (Object)
Default: `undefined`
Use this option to define additional logging levels.
The keys of the object correspond to the namespace of the log level,
and the values should be the numerical value of the level.
```js
const logger = pino({
customLevels: {
foo: 35
}
})
logger.foo('hi')
```
<a id=opt-useOnlyCustomLevels></a>
#### `useOnlyCustomLevels` (Boolean)
Default: `false`
Use this option to only use defined `customLevels` and omit Pino's levels.
Logger's default `level` must be changed to a value in `customLevels` to use `useOnlyCustomLevels`
Warning: this option may not be supported by downstream transports.
```js
const logger = pino({
customLevels: {
foo: 35
},
useOnlyCustomLevels: true,
level: 'foo'
})
logger.foo('hi')
logger.info('hello') // Will throw an error saying info is not found in logger object
```
#### `depthLimit` (Number)
Default: `5`
Option to limit stringification at a specific nesting depth when logging circular objects.
#### `edgeLimit` (Number)
Default: `100`
Option to limit stringification of properties/elements when logging a specific object/array with circular references.
<a id="opt-mixin"></a>
#### `mixin` (Function):
Default: `undefined`
If provided, the `mixin` function is called each time one of the active
logging methods is called. The first parameter is the value `mergeObject` or an empty object. The second parameter is the log level number.
The third parameter is the logger or child logger itself, which can be used to
retrieve logger-specific context from within the `mixin` function.
The function must synchronously return an object. The properties of the returned object will be added to the
logged JSON.
```js
let n = 0
const logger = pino({
mixin () {
return { line: ++n }
}
})
logger.info('hello')
// {"level":30,"time":1573664685466,"pid":78742,"hostname":"x","line":1,"msg":"hello"}
logger.info('world')
// {"level":30,"time":1573664685469,"pid":78742,"hostname":"x","line":2,"msg":"world"}
```
The result of `mixin()` is supposed to be a _new_ object. For performance reason, the object returned by `mixin()` will be mutated by pino.
In the following example, passing `mergingObject` argument to the first `info` call will mutate the global `mixin` object by default:
(* See [`mixinMergeStrategy` option](#opt-mixin-merge-strategy)):
```js
const mixin = {
appName: 'My app'
}
const logger = pino({
mixin() {
return mixin;
}
})
logger.info({
description: 'Ok'
}, 'Message 1')
// {"level":30,"time":1591195061437,"pid":16012,"hostname":"x","appName":"My app","description":"Ok","msg":"Message 1"}
logger.info('Message 2')
// {"level":30,"time":1591195061437,"pid":16012,"hostname":"x","appName":"My app","description":"Ok","msg":"Message 2"}
// Note: the second log contains "description":"Ok" text, even if it was not provided.
```
The `mixin` method can be used to add the level label to each log message such as in the following example:
```js
const logger = pino({
mixin(_context, level) {
return { 'level-label': logger.levels.labels[level] }
}
})
logger.info({
description: 'Ok'
}, 'Message 1')
// {"level":30,"time":1591195061437,"pid":16012,"hostname":"x","description":"Ok","level-label":"info","msg":"Message 1"}
logger.error('Message 2')
// {"level":30,"time":1591195061437,"pid":16012,"hostname":"x","level-label":"error","msg":"Message 2"}
```
If the `mixin` feature is being used merely to add static metadata to each log message,
then a [child logger ⇗](/docs/child-loggers.md) should be used instead. Unless your application
needs to concatenate values for a specific key multiple times, in which case `mixin` can be
used to avoid the [duplicate keys caveat](/docs/child-loggers.md#duplicate-keys-caveat):
```js
const logger = pino({
mixin (obj, num, logger) {
return {
tags: logger.tags
}
}
})
logger.tags = {}
logger.addTag = function (key, value) {
logger.tags[key] = value
}
function createChild (parent, ...context) {
const newChild = logger.child(...context)
newChild.tags = { ...logger.tags }
newChild.addTag = function (key, value) {
newChild.tags[key] = value
}
return newChild
}
logger.addTag('foo', 1)
const child = createChild(logger, {})
child.addTag('bar', 2)
logger.info('this will only have `foo: 1`')
child.info('this will have both `foo: 1` and `bar: 2`')
logger.info('this will still only have `foo: 1`')
```
As of pino 7.x, when the `mixin` is used with the [`nestedKey` option](#opt-nestedkey),
the object returned from the `mixin` method will also be nested. Prior versions would mix
this object into the root.
```js
const logger = pino({
nestedKey: 'payload',
mixin() {
return { requestId: requestId.currentId() }
}
})
logger.info({
description: 'Ok'
}, 'Message 1')
// {"level":30,"time":1591195061437,"pid":16012,"hostname":"x","payload":{"requestId":"dfe9a9014b","description":"Ok"},"msg":"Message 1"}
```
<a id="opt-mixin-merge-strategy"></a>
#### `mixinMergeStrategy` (Function):
Default: `undefined`
If provided, the `mixinMergeStrategy` function is called each time one of the active
logging methods is called. The first parameter is the value `mergeObject` or an empty object,
the second parameter is the value resulting from `mixin()` (* See [`mixin` option](#opt-mixin) or an empty object.
The function must synchronously return an object.
```js
// Default strategy, `mergeObject` has priority
const logger = pino({
mixin() {
return { tag: 'docker' }
},
// mixinMergeStrategy(mergeObject, mixinObject) {
// return Object.assign(mixinMeta, mergeObject)
// }
})
logger.info({
tag: 'local'
}, 'Message')
// {"level":30,"time":1591195061437,"pid":16012,"hostname":"x","tag":"local","msg":"Message"}
```
```js
// Custom mutable strategy, `mixin` has priority
const logger = pino({
mixin() {
return { tag: 'k8s' }
},
mixinMergeStrategy(mergeObject, mixinObject) {
return Object.assign(mergeObject, mixinObject)
}
})
logger.info({
tag: 'local'
}, 'Message')
// {"level":30,"time":1591195061437,"pid":16012,"hostname":"x","tag":"k8s","msg":"Message"}
```
```js
// Custom immutable strategy, `mixin` has priority
const logger = pino({
mixin() {
return { tag: 'k8s' }
},
mixinMergeStrategy(mergeObject, mixinObject) {
return Object.assign({}, mergeObject, mixinObject)
}
})
logger.info({
tag: 'local'
}, 'Message')
// {"level":30,"time":1591195061437,"pid":16012,"hostname":"x","tag":"k8s","msg":"Message"}
```
<a id="opt-redact"></a>
#### `redact` (Array | Object):
Default: `undefined`
As an array, the `redact` option specifies paths that should
have their values redacted from any log output.
Each path must be a string using a syntax that corresponds to JavaScript dot and bracket notation.
If an object is supplied, three options can be specified:
* `paths` (array): Required. An array of paths. See [redaction - Path Syntax ⇗](/docs/redaction.md#paths) for specifics.
* `censor` (String|Function|Undefined): Optional. When supplied as a String the `censor` option will overwrite keys that are to be redacted. When set to `undefined` the key will be removed entirely from the object.
The `censor` option may also be a mapping function. The (synchronous) mapping function has the signature `(value, path) => redactedValue` and is called with the unredacted `value` and `path` to the key being redacted, as an array. For example given a redaction path of `a.b.c` the `path` argument would be `['a', 'b', 'c']`. The value returned from the mapping function becomes the applied censor value. Default: `'[Redacted]'`
value synchronously.
Default: `'[Redacted]'`
* `remove` (Boolean): Optional. Instead of censoring the value, remove both the key and the value. Default: `false`
**WARNING**: Never allow user input to define redacted paths.
* See the [redaction ⇗](/docs/redaction.md) documentation.
* See [fast-redact#caveat ⇗](https://github.com/davidmarkclements/fast-redact#caveat)
<a id=opt-hooks></a>
#### `hooks` (Object)
An object mapping to hook functions. Hook functions allow for customizing
internal logger operations. Hook functions ***must*** be synchronous functions.
<a id="logmethod"></a>
##### `logMethod`
Allows for manipulating the parameters passed to logger methods. The signature
for this hook is `logMethod (args, method, level) {}`, where `args` is an array
of the arguments that were passed to the log method and `method` is the log
method itself, `level` is the log level itself. This hook ***must*** invoke the
`method` function by using apply, like so: `method.apply(this, newArgumentsArray)`.
For example, Pino expects a binding object to be the first parameter with an
optional string message as the second parameter. Using this hook the parameters
can be flipped:
```js
const hooks = {
logMethod (inputArgs, method, level) {
if (inputArgs.length >= 2) {
const arg1 = inputArgs.shift()
const arg2 = inputArgs.shift()
return method.apply(this, [arg2, arg1, ...inputArgs])
}
return method.apply(this, inputArgs)
}
}
```
<a id="streamWrite"></a>
##### `streamWrite`
Allows for manipulating the _stringified_ JSON log data just before writing to various transports.
The method receives the stringified JSON and must return valid stringified JSON.
For example:
```js
const hooks = {
streamWrite (s) {
return s.replaceAll('sensitive-api-key', 'XXX')
}
}
```
<a id=opt-formatters></a>
#### `formatters` (Object)
An object containing functions for formatting the shape of the log lines.
These functions should return a JSONifiable object and
should never throw. These functions allow for full customization of
the resulting log lines. For example, they can be used to change
the level key name or to enrich the default metadata.
##### `level`
Changes the shape of the log level. The default shape is `{ level: number }`.
The function takes two arguments, the label of the level (e.g. `'info'`)
and the numeric value (e.g. `30`).
ps: The log level cannot be customized when using multiple transports
```js
const formatters = {
level (label, number) {
return { level: number }
}
}
```
##### `bindings`
Changes the shape of the bindings. The default shape is `{ pid, hostname }`.
The function takes a single argument, the bindings object, which can be configured
using the [`base` option](#opt-base). Called once when creating logger.
```js
const formatters = {
bindings (bindings) {
return { pid: bindings.pid, hostname: bindings.hostname }
}
}
```
##### `log`
Changes the shape of the log object. This function will be called every time
one of the log methods (such as `.info`) is called. All arguments passed to the
log method, except the message, will be passed to this function. By default, it does
not change the shape of the log object.
```js
const formatters = {
log (object) {
return object
}
}
```
<a id=opt-serializers></a>
#### `serializers` (Object)
Default: `{err: pino.stdSerializers.err}`
An object containing functions for custom serialization of objects.
These functions should return an JSONifiable object and they
should never throw. When logging an object, each top-level property
matching the exact key of a serializer will be serialized using the defined serializer.
The serializers are applied when a property in the logged object matches a property
in the serializers. The only exception is the `err` serializer as it is also applied in case
the object is an instance of `Error`, e.g. `logger.info(new Error('kaboom'))`.
See `errorKey` option to change `err` namespace.
* See [pino.stdSerializers](#pino-stdserializers)
#### `msgPrefix` (String)
Default: `undefined`
The `msgPrefix` property allows you to specify a prefix for every message of the logger and its children.
```js
const logger = pino({
msgPrefix: '[HTTP] '
})
logger.info('got new request!')
// > [HTTP] got new request!
const child = logger.child({})
child.info('User authenticated!')
// > [HTTP] User authenticated!
```
<a id=opt-base></a>
#### `base` (Object)
Default: `{pid: process.pid, hostname: os.hostname()}`
Key-value object added as child logger to each log line.
Set to `undefined` to avoid adding `pid`, `hostname` properties to each log.
#### `enabled` (Boolean)
Default: `true`
Set to `false` to disable logging.
#### `crlf` (Boolean)
Default: `false`
Set to `true` to logs newline delimited JSON with `\r\n` instead of `\n`.
<a id=opt-timestamp></a>
#### `timestamp` (Boolean | Function)
Default: `true`
Enables or disables the inclusion of a timestamp in the
log message. If a function is supplied, it must synchronously return a partial JSON string
representation of the time, e.g. `,"time":1493426328206` (which is the default).
If set to `false`, no timestamp will be included in the output.
See [stdTimeFunctions](#pino-stdtimefunctions) for a set of available functions
for passing in as a value for this option.
Example:
```js
timestamp: () => `,"time":"${new Date(Date.now()).toISOString()}"`
// which is equivalent to:
// timestamp: stdTimeFunctions.isoTime
```
**Caution**: attempting to format time in-process will significantly impact logging performance.
<a id=opt-messagekey></a>
#### `messageKey` (String)
Default: `'msg'`
The string key for the 'message' in the JSON object.
<a id=opt-messagekey></a>
#### `errorKey` (String)
Default: `'err'`
The string key for the 'error' in the JSON object.
<a id=opt-nestedkey></a>
#### `nestedKey` (String)
Default: `null`
If there's a chance that objects being logged have properties that conflict with those from pino itself (`level`, `timestamp`, `pid`, etc)
and duplicate keys in your log records are undesirable, pino can be configured with a `nestedKey` option that causes any `object`s that are logged
to be placed under a key whose name is the value of `nestedKey`.
This way, when searching something like Kibana for values, one can consistently search under the configured `nestedKey` value instead of the root log record keys.
For example,
```js
const logger = require('pino')({
nestedKey: 'payload'
})
const thing = { level: 'hi', time: 'never', foo: 'bar'} // has pino-conflicting properties!
logger.info(thing)
// logs the following:
// {"level":30,"time":1578357790020,"pid":91736,"hostname":"x","payload":{"level":"hi","time":"never","foo":"bar"}}
```
In this way, logged objects' properties don't conflict with pino's standard logging properties,
and searching for logged objects can start from a consistent path.
#### `browser` (Object)
Browser only, may have `asObject` and `write` keys. This option is separately
documented in the [Browser API ⇗](/docs/browser.md) documentation.
* See [Browser API ⇗](/docs/browser.md)
#### `transport` (Object)
The `transport` option is a shorthand for the [pino.transport()](#pino-transport) function.
It supports the same input options:
```js
require('pino')({
transport: {
target: '/absolute/path/to/my-transport.mjs'
}
})
// or multiple transports
require('pino')({
transport: {
targets: [
{ target: '/absolute/path/to/my-transport.mjs', level: 'error' },
{ target: 'some-file-transport', options: { destination: '/dev/null' }
]
}
})
```
If the transport option is supplied to `pino`, a [`destination`](#destination) parameter may not also be passed as a separate argument to `pino`:
```js
pino({ transport: {}}, '/path/to/somewhere') // THIS WILL NOT WORK, DO NOT DO THIS
pino({ transport: {}}, process.stderr) // THIS WILL NOT WORK, DO NOT DO THIS
```
when using the `transport` option. In this case, an `Error` will be thrown.
* See [pino.transport()](#pino-transport)
#### `onChild` (Function)
The `onChild` function is a synchronous callback that will be called on each creation of a new child, passing the child instance as its first argument.
Any error thrown inside the callback will be uncaught and should be handled inside the callback.
```js
const parent = require('pino')({ onChild: (instance) => {
// Execute call back code for each newly created child.
}})
// `onChild` will now be executed with the new child.
parent.child(bindings)
```
<a id="destination"></a>
### `destination` (Number | String | Object | DestinationStream | SonicBoomOpts | WritableStream)
Default: `pino.destination(1)` (STDOUT)
The `destination` parameter can be a file descriptor, a file path, or an
object with `dest` property pointing to a fd or path.
An ordinary Node.js `stream` file descriptor can be passed as the
destination (such as the result
of `fs.createWriteStream`) but for peak log writing performance, it is strongly
recommended to use `pino.destination` to create the destination stream.
Note that the `destination` parameter can be the result of `pino.transport()`.
```js
// pino.destination(1) by default
const stdoutLogger = require('pino')()
// destination param may be in first position when no options:
const fileLogger = require('pino')( pino.destination('/log/path'))
// use the stderr file handle to log to stderr:
const opts = {name: 'my-logger'}
const stderrLogger = require('pino')(opts, pino.destination(2))
// automatic wrapping in pino.destination
const fileLogger = require('pino')('/log/path')
// Asynchronous logging
const fileLogger = pino(pino.destination({ dest: '/log/path', sync: false }))
```
However, there are some special instances where `pino.destination` is not used as the default:
+ When something, e.g a process manager, has monkey-patched `process.stdout.write`.
In these cases `process.stdout` is used instead.
Note: If the parameter is a string integer, e.g. `'1'`, it will be coerced to
a number and used as a file descriptor. If this is not desired, provide a full
path, e.g. `/tmp/1`.
* See [`pino.destination`](#pino-destination)
<a id="metadata"></a>
#### `destination[Symbol.for('pino.metadata')]`
Default: `false`
Using the global symbol `Symbol.for('pino.metadata')` as a key on the `destination` parameter and
setting the key to `true`, indicates that the following properties should be
set on the `destination` object after each log line is written:
* the last logging level as `destination.lastLevel`
* the last logging message as `destination.lastMsg`
* the last logging object as `destination.lastObj`
* the last time as `destination.lastTime`, which will be the partial string returned
by the time function.
* the last logger instance as `destination.lastLogger` (to support child
loggers)
The following is a succinct usage example:
```js
const dest = pino.destination('/dev/null')
dest[Symbol.for('pino.metadata')] = true
const logger = pino(dest)
logger.info({a: 1}, 'hi')
const { lastMsg, lastLevel, lastObj, lastTime} = dest
console.log(
'Logged message "%s" at level %d with object %o at time %s',
lastMsg, lastLevel, lastObj, lastTime
) // Logged message "hi" at level 30 with object { a: 1 } at time 1531590545089
```
<a id="logger"></a>
## Logger Instance
The logger instance is the object returned by the main exported
[`pino`](#export) function.
The primary purpose of the logger instance is to provide logging methods.
The default logging methods are `trace`, `debug`, `info`, `warn`, `error`, and `fatal`.
Each logging method has the following signature:
`([mergingObject], [message], [...interpolationValues])`.
The parameters are explained below using the `logger.info` method but the same applies to all logging methods.
### Logging Method Parameters
<a id=mergingobject></a>
#### `mergingObject` (Object)
An object can optionally be supplied as the first parameter. Each enumerable key and value
of the `mergingObject` is copied into the JSON log line.
```js
logger.info({MIX: {IN: true}})
// {"level":30,"time":1531254555820,"pid":55956,"hostname":"x","MIX":{"IN":true}}
```
If the object is of type Error, it is wrapped in an object containing a property err (`{ err: mergingObject }`).
This allows for a unified error handling flow.
Options `serializers` and `errorKey` could be used at instantiation time to change the namespace
from `err` to another string as preferred.
<a id="message"></a>
#### `message` (String)
A `message` string can optionally be supplied as the first parameter, or
as the second parameter after supplying a `mergingObject`.
By default, the contents of the `message` parameter will be merged into the
JSON log line under the `msg` key:
```js
logger.info('hello world')
// {"level":30,"time":1531257112193,"msg":"hello world","pid":55956,"hostname":"x"}
```
The `message` parameter takes precedence over the `mergingObject`.
That is, if a `mergingObject` contains a `msg` property, and a `message` parameter
is supplied in addition, the `msg` property in the output log will be the value of
the `message` parameter not the value of the `msg` property on the `mergingObject`.
See [Avoid Message Conflict](/docs/help.md#avoid-message-conflict) for information
on how to overcome this limitation.
If no `message` parameter is provided, and the `mergingObject` is of type `Error` or it has a property named `err`, the
`message` parameter is set to the `message` value of the error. See option `errorKey` if you want to change the namespace.
The `messageKey` option can be used at instantiation time to change the namespace
from `msg` to another string as preferred.
The `message` string may contain a printf style string with support for
the following placeholders:
* `%s` – string placeholder, every non-string value passed in will have `.toString()` called.
* `%d` – digit placeholder
* `%O`, `%o`, and `%j` – object placeholder
Values supplied as additional arguments to the logger method will
then be interpolated accordingly.
* See [`messageKey` pino option](#opt-messagekey)
* See [`...interpolationValues` log method parameter](#interpolationvalues)
<a id="interpolationvalues"></a>
#### `...interpolationValues` (Any)
All arguments supplied after `message` are serialized and interpolated according
to any supplied printf-style placeholders (`%s`, `%d`, `%o`|`%O`|`%j`) to form
the final output `msg` value for the JSON log line.
```js
logger.info('%o hello %s', {worldly: 1}, 'world')
// {"level":30,"time":1531257826880,"msg":"{\"worldly\":1} hello world","pid":55956,"hostname":"x"}
```
Since pino v6, we do not automatically concatenate and cast to string
consecutive parameters:
```js
logger.info('hello', 'world')
// {"level":30,"time":1531257618044,"msg":"hello","pid":55956,"hostname":"x"}
// world is missing
```
However, it's possible to inject a hook to modify this behavior:
```js
const pinoOptions = {
hooks: { logMethod }
}
function logMethod (args, method) {
if (args.length === 2) {
args[0] = `${args[0]} %j`
}
method.apply(this, args)
}
const logger = pino(pinoOptions)
```
* See [`message` log method parameter](#message)
* See [`logMethod` hook](#logmethod)
<a id="error-serialization"></a>
#### Errors
Errors can be supplied as either the first parameter or if already using `mergingObject` then as the `err` property on the `mergingObject`.
Options `serializers` and `errorKey` could be used at instantiation time to change the namespace
from `err` to another string as preferred.
> ## Note
> This section describes the default configuration. The error serializer can be
> mapped to a different key using the [`serializers`](#opt-serializers) option.
```js
logger.info(new Error("test"))
// {"level":30,"time":1531257618044,"msg":"test","stack":"...","type":"Error","pid":55956,"hostname":"x"}
logger.info({ err: new Error("test"), otherkey: 123 }, "some text")
// {"level":30,"time":1531257618044,"err":{"msg": "test", "stack":"...","type":"Error"},"msg":"some text","pid":55956,"hostname":"x","otherkey":123}
```
<a id="trace"></a>
### `logger.trace([mergingObject], [message], [...interpolationValues])`
Write a `'trace'` level log, if the configured [`level`](#level) allows for it.
* See [`mergingObject` log method parameter](#mergingobject)
* See [`message` log method parameter](#message)
* See [`...interpolationValues` log method parameter](#interpolationvalues)
<a id="debug"></a>
### `logger.debug([mergingObject], [message], [...interpolationValues])`
Write a `'debug'` level log, if the configured `level` allows for it.
* See [`mergingObject` log method parameter](#mergingobject)
* See [`message` log method parameter](#message)
* See [`...interpolationValues` log method parameter](#interpolationvalues)
<a id="info"></a>
### `logger.info([mergingObject], [message], [...interpolationValues])`
Write an `'info'` level log, if the configured `level` allows for it.
* See [`mergingObject` log method parameter](#mergingobject)
* See [`message` log method parameter](#message)
* See [`...interpolationValues` log method parameter](#interpolationvalues)
<a id="warn"></a>
### `logger.warn([mergingObject], [message], [...interpolationValues])`
Write a `'warn'` level log, if the configured `level` allows for it.
* See [`mergingObject` log method parameter](#mergingobject)
* See [`message` log method parameter](#message)
* See [`...interpolationValues` log method parameter](#interpolationvalues)
<a id="error"></a>
### `logger.error([mergingObject], [message], [...interpolationValues])`
Write a `'error'` level log, if the configured `level` allows for it.
* See [`mergingObject` log method parameter](#mergingobject)
* See [`message` log method parameter](#message)
* See [`...interpolationValues` log method parameter](#interpolationvalues)
<a id="fatal"></a>
### `logger.fatal([mergingObject], [message], [...interpolationValues])`
Write a `'fatal'` level log, if the configured `level` allows for it.
Since `'fatal'` level messages are intended to be logged just before the process exiting the `fatal`
method will always sync flush the destination.
Therefore it's important not to misuse `fatal` since
it will cause performance overhead if used for any
other purpose than writing final log messages before
the process crashes or exits.
* See [`mergingObject` log method parameter](#mergingobject)
* See [`message` log method parameter](#message)
* See [`...interpolationValues` log method parameter](#interpolationvalues)
<a id="silent"><a>
### `logger.silent()`
Noop function.
<a id="child"></a>
### `logger.child(bindings, [options]) => logger`
The `logger.child` method allows for the creation of stateful loggers,
where key-value pairs can be pinned to a logger causing them to be output
on every log line.
Child loggers use the same output stream as the parent and inherit
the current log level of the parent at the time they are spawned.
The log level of a child is mutable. It can be set independently
of the parent either by setting the [`level`](#level) accessor after creating
the child logger or using the [`options.level`](#optionslevel-string) key.
<a id="logger-child-bindings"></a>
#### `bindings` (Object)
An object of key-value pairs to include in every log line output
via the returned child logger.
```js
const child = logger.child({ MIX: {IN: 'always'} })
child.info('hello')
// {"level":30,"time":1531258616689,"msg":"hello","pid":64849,"hostname":"x","MIX":{"IN":"always"}}
child.info('child!')
// {"level":30,"time":1531258617401,"msg":"child!","pid":64849,"hostname":"x","MIX":{"IN":"always"}}
```
The `bindings` object may contain any key except for reserved configuration keys `level` and `serializers`.
##### `bindings.serializers` (Object) - DEPRECATED
Use `options.serializers` instead.
#### `options` (Object)
Options for child logger. These options will override the parent logger options.
##### `options.level` (String)
The `level` property overrides the log level of the child logger.
By default, the parent log level is inherited.
After the creation of the child logger, it is also accessible using the [`logger.level`](#logger-level) key.
```js
const logger = pino()
logger.debug('nope') // will not log, since default level is info
const child = logger.child({foo: 'bar'}, {level: 'debug'})
child.debug('debug!') // will log as the `level` property set the level to debug
```
<a id="options-msgPrefix"></a>
##### `options.msgPrefix` (String)
Default: `undefined`
The `msgPrefix` property allows you to specify a prefix for every message of the child logger.
By default, the parent prefix is inherited.
If the parent already has a prefix, the prefix of the parent and then the child will be displayed.
```js
const logger = pino({
msgPrefix: '[HTTP] '
})
logger.info('got new request!')
// > [HTTP] got new request!
const child = logger.child({avengers: 'assemble'}, {msgPrefix: '[Proxy] '})
child.info('message proxied!')
// > [HTTP] [Proxy] message proxied!
```
##### `options.redact` (Array | Object)
Setting `options.redact` to an array or object will override the parent `redact` options. To remove `redact` options inherited from the parent logger set this value as an empty array (`[]`).
```js
const logger = require('pino')({ redact: ['hello'] })
logger.info({ hello: 'world' })
// {"level":30,"time":1625794363403,"pid":67930,"hostname":"x","hello":"[Redacted]"}
const child = logger.child({ foo: 'bar' }, { redact: ['foo'] })
logger.info({ hello: 'world' })
// {"level":30,"time":1625794553558,"pid":67930,"hostname":"x","hello":"world", "foo": "[Redacted]" }
```
* See [`redact` option](#opt-redact)
##### `options.serializers` (Object)
Child loggers inherit the [serializers](#opt-serializers) from the parent logger.
Setting the `serializers` key of the `options` object will override
any configured parent serializers.
```js
const logger = require('pino')()
logger.info({test: 'will appear'})
// {"level":30,"time":1531259759482,"pid":67930,"hostname":"x","test":"will appear"}
const child = logger.child({}, {serializers: {test: () => `child-only serializer`}})
child.info({test: 'will be overwritten'})
// {"level":30,"time":1531259784008,"pid":67930,"hostname":"x","test":"child-only serializer"}
```
* See [`serializers` option](#opt-serializers)
* See [pino.stdSerializers](#pino-stdSerializers)
<a id="logger-bindings"></a>
### `logger.bindings()`
Returns an object containing all the current bindings, cloned from the ones passed in via `logger.child()`.
```js
const child = logger.child({ foo: 'bar' })
console.log(child.bindings())
// { foo: 'bar' }
const anotherChild = child.child({ MIX: { IN: 'always' } })
console.log(anotherChild.bindings())
// { foo: 'bar', MIX: { IN: 'always' } }
```
<a id="logger-set-bindings"></a>
### `logger.setBindings(bindings)`
Adds to the bindings of this logger instance.
**Note:** Does not overwrite bindings. Can potentially result in duplicate keys in
log lines.
* See [`bindings` parameter in `logger.child`](#logger-child-bindings)
<a id="flush"></a>
### `logger.flush([cb])`
Flushes the content of the buffer when using `pino.destination({
sync: false })`.
This is an asynchronous, best used as fire and forget, operation.
The use case is primarily for asynchronous logging, which may buffer
log lines while others are being written. The `logger.flush` method can be
used to flush the logs
on a long interval, say ten seconds. Such a strategy can provide an
optimum balance between extremely efficient logging at high demand periods
and safer logging at low demand periods.
If there is a need to wait for the logs to be flushed, a callback should be used.
**Note:** `flush()` does not work when using `pino-pretty`. See
[Flush Limitations with `pino-pretty`](/docs/asynchronous.md) for more details.
* See [`destination` parameter](#destination)
* See [Asynchronous Logging ⇗](./asynchronous.md)
<a id="logger-level"></a>
### `logger.level` (String) [Getter/Setter]
Set this property to the desired logging level.
The core levels and their values are as follows:
| | | | | | | | |
|:-----------|-------|-------|------|------|-------|-------|---------:|
| **Level:** | trace | debug | info | warn | error | fatal | silent |
| **Value:** | 10 | 20 | 30 | 40 | 50 | 60 | Infinity |
The logging level is a *minimum* level based on the associated value of that level.
For instance if `logger.level` is `info` *(30)* then `info` *(30)*, `warn` *(40)*, `error` *(50)*, and `fatal` *(60)* log methods will be enabled but the `trace` *(10)* and `debug` *(20)* methods, being less than 30, will not.
The `silent` logging level is a specialized level that will disable all logging,
the `silent` log method is a noop function.
<a id="islevelenabled"></a>
### `logger.isLevelEnabled(level)`
A utility method for determining if a given log level will write to the destination.
#### `level` (String)
The given level to check against:
```js
if (logger.isLevelEnabled('debug')) logger.debug('conditional log')
```
#### `levelLabel` (String)
Defines the method name of the new level.
* See [`logger.level`](#level)
#### `levelValue` (Number)
Defines the associated minimum threshold value for the level, and
therefore where it sits in order of priority among other levels.
* See [`logger.level`](#level)
<a id="levelVal"></a>
### `logger.levelVal` (Number)
Supplies the integer value for the current logging level.
```js
if (logger.levelVal === 30) {
console.log('logger level is `info`')
}
```
<a id="levels"></a>
### `logger.levels` (Object)
Levels are mapped to values to determine the minimum threshold that a
logging method should be enabled at (see [`logger.level`](#level)).
The `logger.levels` property holds the mappings between levels and values,
and vice versa.
```sh
$ node -p "require('pino')().levels"
```
```js
{ labels:
{ '10': 'trace',
'20': 'debug',
'30': 'info',
'40': 'warn',
'50': 'error',
'60': 'fatal' },
values:
{ fatal: 60, error: 50, warn: 40, info: 30, debug: 20, trace: 10 } }
```
* See [`logger.level`](#level)
<a id="serializers"></a>
### logger\[Symbol.for('pino.serializers')\]
Returns the serializers as applied to the current logger instance. If a child logger did not
register its own serializer upon instantiation the serializers of the parent will be returned.
<a id="level-change"></a>
### Event: 'level-change'
The logger instance is also an [`EventEmitter ⇗`](https://nodejs.org/dist/latest/docs/api/events.html#events_class_eventemitter)
A listener function can be attached to a logger via the `level-change` event
The listener is passed five arguments:
* `levelLabel` – the new level string, e.g `trace`
* `levelValue` – the new level number, e.g `10`
* `previousLevelLabel` – the prior level string, e.g `info`
* `previousLevelValue` – the prior level number, e.g `30`
* `logger` – the logger instance from which the event originated
```js
const logger = require('pino')()
logger.on('level-change', (lvl, val, prevLvl, prevVal) => {
console.log('%s (%d) was changed to %s (%d)', prevLvl, prevVal, lvl, val)
})
logger.level = 'trace' // trigger event
```
Please note that due to a [known bug](https://github.com/pinojs/pino/issues/1006), every `logger.child()` call will
fire a `level-change` event. These events can be ignored by writing an event handler like:
```js
const logger = require('pino')()
logger.on('level-change', function (lvl, val, prevLvl, prevVal, instance) {
if (logger !== instance) {
return
}
console.log('%s (%d) was changed to %s (%d)', prevLvl, prevVal, lvl, val)
})
logger.child({}); // trigger an event by creating a child instance, notice no console.log
logger.level = 'trace' // trigger event using actual value change, notice console.log
```
<a id="version"></a>
### `logger.version` (String)
Exposes the Pino package version. Also available on the exported `pino` function.
* See [`pino.version`](#pino-version)
<a id="msgPrefix"></a>
### `logger.msgPrefix` (String|Undefined)
Exposes the cumulative `msgPrefix` of the logger.
* See [`options.msgPrefix`](#options-msgPrefix)
## Statics
<a id="pino-destination"></a>
### `pino.destination([opts]) => SonicBoom`
Create a Pino Destination instance: a stream-like object with
significantly more throughput than a standard Node.js stream.
```js
const pino = require('pino')
const logger = pino(pino.destination('./my-file'))
const logger2 = pino(pino.destination())
const logger3 = pino(pino.destination({
dest: './my-file',
minLength: 4096, // Buffer before writing
sync: false // Asynchronous logging, the default
}))
const logger4 = pino(pino.destination({
dest: './my-file2',
sync: true // Synchronous logging
}))
```
The `pino.destination` method may be passed a file path or a numerical file descriptor.
By default, `pino.destination` will use `process.stdout.fd` (1) as the file descriptor.
`pino.destination` is implemented on [`sonic-boom` ⇗](https://github.com/mcollina/sonic-boom).
A `pino.destination` instance can also be used to reopen closed files
(for example, for some log rotation scenarios), see [Reopening log files](/docs/help.md#reopening).
* See [`destination` parameter](#destination)
* See [`sonic-boom` ⇗](https://github.com/mcollina/sonic-boom)
* See [Reopening log files](/docs/help.md#reopening)
* See [Asynchronous Logging ⇗](/docs/asynchronous.md)
<a id="pino-transport"></a>
### `pino.transport(options) => ThreadStream`
Create a stream that routes logs to a worker thread that
wraps around a [Pino Transport](/docs/transports.md).
```js
const pino = require('pino')
const transport = pino.transport({
target: 'some-transport',
options: { some: 'options for', the: 'transport' }
})
pino(transport)
```
Multiple transports may also be defined, and specific levels can be logged to each transport:
```js
const pino = require('pino')
const transport = pino.transport({
targets: [{
level: 'info',
target: 'pino-pretty' // must be installed separately
}, {
level: 'trace',
target: 'pino/file',
options: { destination: '/path/to/store/logs' }
}]
})
pino(transport)
```
A pipeline could also be created to transform log lines _before_ sending them:
```js
const pino = require('pino')
const transport = pino.transport({
pipeline: [{
target: 'pino-syslog' // must be installed separately
}, {
target: 'pino-socket' // must be installed separately
}]
})
pino(transport)
```
Multiple transports can now be defined to include pipelines:
```js
const pino = require('pino')
const transport = pino.transport({
targets: [{
level: 'info',
target: 'pino-pretty' // must be installed separately
}, {
level: 'trace',
target: 'pino/file',
options: { destination: '/path/to/store/logs' }
}, {
pipeline: [{
target: 'pino-syslog' // must be installed separately
}, {
target: 'pino-socket' // must be installed separately
}]
}
]
})
pino(transport)
```
If `WeakRef`, `WeakMap`, and `FinalizationRegistry` are available in the current runtime (v14.5.0+), then the thread
will be automatically terminated in case the stream or logger goes out of scope.
The `transport()` function adds a listener to `process.on('beforeExit')` and `process.on('exit')` to ensure the worker
is flushed and all data synced before the process exits.
Note that calling `process.exit()` on the main thread will stop the event loop on the main thread from turning. As a result,
using `console.log` and `process.stdout` after the main thread called `process.exit()` will not produce any output.
If you are embedding/integrating pino within your framework, you will need to make pino aware of the script that is calling it,
like so:
```js
const pino = require('pino')
const getCaller = require('get-caller-file')
module.exports = function build () {
const logger = pino({
transport: {
caller: getCaller(),
target: 'transport',
options: { destination: './destination' }
}
})
return logger
}
```
Note that _any `'error'`_ event emitted by the transport must be considered a fatal error and the process must be terminated.
Error events are not recoverable.
For more on transports, how they work, and how to create them see the [`Transports documentation`](/docs/transports.md).
* See [`Transports`](/docs/transports.md)
* See [`thread-stream` ⇗](https://github.com/mcollina/thread-stream)
#### Options
* `target`: The transport to pass logs through. This may be an installed module name or an absolute path.
* `options`: An options object which is serialized (see [Structured Clone Algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm)), passed to the worker thread, parsed and then passed to the exported transport function.
* `worker`: [Worker thread](https://nodejs.org/api/worker_threads.html#worker_threads_new_worker_filename_options) configuration options. Additionally, the `worker` option supports `worker.autoEnd`. If this is set to `false` logs will not be flushed on process exit. It is then up to the developer to call `transport.end()` to flush logs.
* `targets`: May be specified instead of `target`. Must be an array of transport configurations and/or pipelines. Transport configurations include the aforementioned `options` and `target` options plus a `level` option which will send only logs above a specified level to that transport. If `level` is omitted, it defaults to `info`.
* `pipeline`: May be specified instead of `target`. Must be an array of transport configurations. Transport configurations include the aforementioned `options` and `target` options. All intermediate steps in the pipeline _must_ be `Transform` streams and not `Writable`.
* `dedupe`: See [pino.multistream options](#pino-multistream)
Notes:
- The top-level `level` in `pino.transport({ ... })` is not used for filtering.
- With a single `target` (or a single `pipeline`), filtering is controlled by `logger.level`.
- Per-transport level filtering is applied when using `targets` (multi-destination mode).
<a id="pino-multistream"></a>
### `pino.multistream(streamsArray, opts) => MultiStreamRes`
Create a stream composed by multiple destination streams and returns an
object implementing the [MultiStreamRes](#multistreamres) interface.
```js
var fs = require('node:fs')
var pino = require('pino')
var pretty = require('pino-pretty')
var streams = [
{stream: fs.createWriteStream('/tmp/info.stream.out')},
{stream: pretty() },
{level: 'debug', stream: fs.createWriteStream('/tmp/debug.stream.out')},
{level: 'fatal', stream: fs.createWriteStream('/tmp/fatal.stream.out')}
]
var log = pino({
level: 'debug' // this MUST be set at the lowest level of the
// destinations
}, pino.multistream(streams))
log.debug('this will be written to /tmp/debug.stream.out')
log.info('this will be written to /tmp/debug.stream.out and /tmp/info.stream.out')
log.fatal('this will be written to /tmp/debug.stream.out, /tmp/info.stream.out and /tmp/fatal.stream.out')
```
In order for `multistream` to work, the log level __must__ be set to the lowest level used in the streams array. Default is `info`.
#### Options
* `levels`: Pass custom log level definitions to the instance as an object.
+ `dedupe`: Set this to `true` to send logs only to the stream with the higher level. Default: `false`
`dedupe` flag can be useful for example when using `pino.multistream` to redirect `error` logs to `process.stderr` and others to `process.stdout`:
```js
var pino = require('pino')
var multistream = pino.multistream
var streams = [
{level: 'debug', stream: process.stdout},
{level: 'error', stream: process.stderr},
]
var opts = {
levels: {
silent: Infinity,
fatal: 60,
error: 50,
warn: 50,
info: 30,
debug: 20,
trace: 10
},
dedupe: true,
}
var log = pino({
level: 'debug' // this MUST be set at the lowest level of the
// destinations
}, multistream(streams, opts))
log.debug('this will be written ONLY to process.stdout')
log.info('this will be written ONLY to process.stdout')
log.error('this will be written ONLY to process.stderr')
log.fatal('this will be written ONLY to process.stderr')
```
<a id="pino-stdserializers"></a>
### `pino.stdSerializers` (Object)
The `pino.stdSerializers` object provides functions for serializing objects common to many projects. The standard serializers are directly imported from [pino-std-serializers](https://github.com/pinojs/pino-std-serializers).
* See [pino-std-serializers ⇗](https://github.com/pinojs/pino-std-serializers)
<a id="pino-stdtimefunctions"></a>
### `pino.stdTimeFunctions` (Object)
The [`timestamp`](#opt-timestamp) option can accept a function that determines the
`timestamp` value in a log line.
The `pino.stdTimeFunctions` object provides a very small set of common functions for generating the
`timestamp` property. These consist of the following
* `pino.stdTimeFunctions.epochTime`: Milliseconds since Unix epoch (Default)
* `pino.stdTimeFunctions.unixTime`: Seconds since Unix epoch
* `pino.stdTimeFunctions.nullTime`: Clears timestamp property (Used when `timestamp: false`)
* `pino.stdTimeFunctions.isoTime`: ISO 8601-formatted time in UTC
* `pino.stdTimeFunctions.isoTimeNano`: RFC 3339-formatted time in UTC with nanosecond precision
* See [`timestamp` option](#opt-timestamp)
<a id="pino-symbols"></a>
### `pino.symbols` (Object)
For integration purposes with ecosystem and third-party libraries `pino.symbols`
exposes the symbols used to hold non-public state and methods on the logger instance.
Access to the symbols allows logger state to be adjusted, and methods to be overridden or
proxied for performant integration where necessary.
The `pino.symbols` object is intended for library implementers and shouldn't be utilized
for general use.
<a id="pino-version"></a>
### `pino.version` (String)
Exposes the Pino package version. Also available on the logger instance.
* See [`logger.version`](#version)
## Interfaces
<a id="pino-multistreamres"></a>
### `MultiStreamRes`
Properties:
* `write(data)`
- `data` Object | string
- Returns: void
Write `data` onto the streams held by the current instance.
* `add(dest)`
- `dest` [StreamEntry](#streamentry) | [DestinationStream](#destinationstream)
- Returns: [MultiStreamRes](#multistreamres)
Add `dest` stream to the array of streams of the current instance.
* `flushSync()`
- Returns: `undefined`
Call `flushSync` on each stream held by the current instance.
* `lastId`
- number
The ID assigned to the last stream assigned to the current instance.
* `minLevel`
- number
The minimum level amongst all the streams held by the current instance.
* `remove(id)`
- `id` [number]
Removes a stream from the array of streams of the current instance using its assigned ID.
* `streams`
- Returns: [StreamEntry[]](#streamentry)
The array of streams currently held by the current instance.
* `clone(level)`
- `level` [Level](#level-1)
- Returns: [MultiStreamRes](#multistreamres)
Returns a cloned object of the current instance but with the provided `level`.
### `StreamEntry`
Properties:
* `stream`
- DestinationStream
* `level`
- Optional: [Level](#level-1)
### `DestinationStream`
Properties:
* `write(msg)`
- `msg` string
## Types
### `Level`
* Values: `"fatal"` | `"error"` | `"warn"` | `"info"` | `"debug"` | `"trace"`
## TypeScript
### Module Augmentation
Pino supports TypeScript module augmentation to extend its type definitions. This allows you to customize the logging behavior to fit your application's specific requirements.
#### `LogFnFields` Interface
The `LogFnFields` interface can be augmented to control what fields are allowed in logging method objects. This is particularly useful for:
- Preventing certain fields from being logged (for security or compliance reasons)
- Enforcing specific field types across your application
- Enforcing consistent structured logging
##### Banning Fields
You can ban specific fields from being passed to logging methods by setting them to `never`. This helps prevent users from unintentionally overriding fields that are already set in the logger's `base` option, or clarifies that these fields are predefined.
```typescript
declare module "pino" {
interface LogFnFields {
service?: never;
version?: never;
}
}
// These will now cause TypeScript errors
logger.info({ service: 'other-api', message: 'success' }) // ❌
logger.info({ message: 'success' }) // ✅
```
##### Enforcing Field Types
You can also enforce specific types for certain fields:
```typescript
declare module "pino" {
interface LogFnFields {
userId?: string;
requestId?: string;
}
}
// These will cause TypeScript errors
logger.info({ userId: 123 }) // ❌ Error: userId must be string
logger.info({ requestId: null }) // ❌ Error: requestId must be string
// This works fine
logger.info({ userId: '123' }) // ✅ Works fine
```
##### Enforcing Structured Logging
Required fields (non-optional) enforce consistent structured logging by requiring specific fields in all log objects:
```typescript
declare module "pino" {
interface LogFnFields {
userId: string
}
}
logger.info({ userId: '123' }) // ✅ Works fine
logger.info({}) // ❌ Property 'userId' is missing in type '{}'
```
**Note**: Required fields will cause TypeScript errors when logging certain types like `Error` objects that don't contain the required properties:
```typescript
logger.error(new Error('test')) // ❌ Property 'userId' is missing in type 'Error'
```
This ensures that all log entries include required context fields, promoting consistent logging practices.
================================================
FILE: docs/asynchronous.md
================================================
# Asynchronous Logging
Asynchronous logging enables the minimum overhead of Pino.
Asynchronous logging works by buffering log messages and writing them in larger chunks.
```js
const pino = require('pino')
const logger = pino(pino.destination({
dest: './my-file', // omit for stdout
minLength: 4096, // Buffer before writing
sync: false // Asynchronous logging
}))
```
It's always possible to turn on synchronous logging by passing `sync: true`.
In this mode of operation, log messages are directly written to the
output stream as the messages are generated with a _blocking_ operation.
* See [`pino.destination`](/docs/api.md#pino-destination)
* `pino.destination` is implemented on [`sonic-boom` ⇗](https://github.com/mcollina/sonic-boom).
### AWS Lambda
Asynchronous logging is disabled by default on AWS Lambda or any other environment
that modifies `process.stdout`. If forcefully turned on, we recommend calling `dest.flushSync()` at the end
of each function execution to avoid losing data.
## Caveats
Asynchronous logging has a couple of important caveats:
* As opposed to the synchronous mode, there is not a one-to-one relationship between
calls to logging methods (e.g. `logger.info`) and writes to a log file
* There is a possibility of the most recently buffered log messages being lost
in case of a system failure, e.g. a power cut.
### Flush Limitations with `pino-pretty`
The `logger.flush()` method does not work when using `pino-pretty` because:
1. **Transport Architecture**: `pino-pretty` runs in a separate worker thread via the transport mechanism.
2. **Buffer Flow**: When you call `logger.flush()`, it flushes the SonicBoom destination in the main thread, but the logs remain queued in the thread-stream worker waiting to be processed by `pino-pretty`.
3. **No Cross-Thread Flush**: The flush operation never propagates through to the worker thread where the pretty printer is processing the output.
This means that even with `logger.flush()`, your formatted logs may not appear immediately, and the flush will only ensure the main thread buffer is written, not the formatted output.
See also:
* [`pino.destination` API](/docs/api.md#pino-destination)
* [`destination` parameter](/docs/api.md#destination)
================================================
FILE: docs/benchmarks.md
================================================
# Benchmarks
`pino.info('hello world')`:
```
BASIC benchmark averages
Bunyan average: 377.434ms
Winston average: 270.249ms
Bole average: 172.690ms
Debug average: 220.527ms
LogLevel average: 222.802ms
Pino average: 114.801ms
PinoMinLength average: 70.968ms
PinoNodeStream average: 159.192ms
```
`pino.info({'hello': 'world'})`:
```
OBJECT benchmark averages
BunyanObj average: 410.379ms
WinstonObj average: 273.120ms
BoleObj average: 185.069ms
LogLevelObject average: 433.425ms
PinoObj average: 119.315ms
PinoMinLengthObj average: 76.968ms
PinoNodeStreamObj average: 164.268ms
```
`pino.info(aBigDeeplyNestedObject)`:
```
DEEP-OBJECT benchmark averages
BunyanDeepObj average: 1.839ms
WinstonDeepObj average: 5.604ms
BoleDeepObj average: 3.422ms
LogLevelDeepObj average: 11.716ms
PinoDeepObj average: 2.256ms
PinoMinLengthDeepObj average: 2.240ms
PinoNodeStreamDeepObj average: 2.595ms
```
`pino.info('hello %s %j %d', 'world', {obj: true}, 4, {another: 'obj'})`:
For a fair comparison, [LogLevel](http://npm.im/loglevel) was extended
to include a timestamp and [bole](http://npm.im/bole) had
`fastTime` mode switched on.
================================================
FILE: docs/browser.md
================================================
# Browser API
Pino is compatible with [`browserify`](https://npm.im/browserify) for browser-side usage:
This can be useful with isomorphic/universal JavaScript code.
By default, in the browser,
`pino` uses corresponding [Log4j](https://en.wikipedia.org/wiki/Log4j) `console` methods (`console.error`, `console.warn`, `console.info`, `console.debug`, `console.trace`) and uses `console.error` for any `fatal` level logs.
## Options
Pino can be passed a `browser` object in the options object,
which can have the following properties:
### `asObject` (Boolean)
```js
const pino = require('pino')({browser: {asObject: true}})
```
The `asObject` option will create a pino-like log object instead of
passing all arguments to a console method, for instance:
```js
pino.info('hi') // creates and logs {msg: 'hi', level: 30, time: <ts>}
```
When `write` is set, `asObject` will always be `true`.
### `asObjectBindingsOnly` (Boolean)
```js
const pino = require('pino')({browser: {asObjectBindingsOnly: true}})
```
The `asObjectBindingsOnly` option is similar to `asObject` but will keep the message
and arguments unformatted. This allows to defer formatting the message to the
actual call to `console` methods, where browsers then have richer formatting in
their devtools than when pino will format the message to a string first.
```js
pino.info('hello %s', 'world') // creates and logs {level: 30, time: <ts>}, 'hello %s', 'world'
```
### `formatters` (Object)
An object containing functions for formatting the shape of the log lines. When provided, it enables the logger to produce a pino-like log object with customized formatting. Currently, it supports formatting for the `level` object only.
##### `level`
Changes the shape of the log level. The default shape is `{ level: number }`.
The function takes two arguments, the label of the level (e.g. `'info'`)
and the numeric value (e.g. `30`).
```js
const formatters = {
level (label, number) {
return { level: number }
}
}
```
### `reportCaller` (Boolean)
Attempts to capture and include the originating callsite (file:line:column) for each log call in the browser logger.
- When used together with `asObject` (or when `formatters` are provided), the callsite is added as a `caller` string property on the emitted log object.
- In the default mode (non‑object), the callsite string is appended as the last argument passed to the corresponding `console` method. This makes the location visible in the console output even though the console’s clickable header still points to Pino internals.
```js
// Object mode: adds `caller` to the log object
const pino = require('pino')({
browser: {
asObject: true,
reportCaller: true
}
})
pino.info('hello')
// -> { level: 30, msg: 'hello', time: <ts>, caller: '/path/to/file.js:10:15' }
// Default mode: appends the caller string as the last console argument
const pino2 = require('pino')({
browser: {
reportCaller: true
}
})
pino2.info('hello')
// -> console receives: 'hello', '/path/to/file.js:10:15'
```
Notes:
- This is a best‑effort feature that parses the JavaScript Error stack. Stack formats vary across engines.
- The clickable link shown by devtools for a console message is determined by where `console.*` is invoked and cannot be changed by libraries; `reportCaller` surfaces the user callsite alongside the log message.
### `write` (Function | Object)
Instead of passing log messages to `console.log` they can be passed to
a supplied function.
If `write` is set to a single function, all logging objects are passed
to this function.
```js
const pino = require('pino')({
browser: {
write: (o) => {
// do something with o
}
}
})
```
If `write` is an object, it can have methods that correspond to the
levels. When a message is logged at a given level, the corresponding
method is called. If a method isn't present, the logging falls back
to using the `console`.
```js
const pino = require('pino')({
browser: {
write: {
info: function (o) {
//process info log object
},
error: function (o) {
//process error log object
}
}
}
})
```
### `serialize`: (Boolean | Array)
The serializers provided to `pino` are ignored by default in the browser, including
the standard serializers provided with Pino. Since the default destination for log
messages is the console, values such as `Error` objects are enhanced for inspection,
which they otherwise wouldn't be if the Error serializer was enabled.
We can turn all serializers on,
```js
const pino = require('pino')({
browser: {
serialize: true
}
})
```
Or we can selectively enable them via an array:
```js
const pino = require('pino')({
serializers: {
custom: myCustomSerializer,
another: anotherSerializer
},
browser: {
serialize: ['custom']
}
})
// following will apply myCustomSerializer to the custom property,
// but will not apply anotherSerializer to another key
pino.info({custom: 'a', another: 'b'})
```
When `serialize` is `true` the standard error serializer is also enabled (see https://github.com/pinojs/pino/blob/master/docs/api.md#stdSerializers).
This is a global serializer, which will apply to any `Error` objects passed to the logger methods.
If `serialize` is an array the standard error serializer is also automatically enabled, it can
be explicitly disabled by including a string in the serialize array: `!stdSerializers.err`, like so:
```js
const pino = require('pino')({
serializers: {
custom: myCustomSerializer,
another: anotherSerializer
},
browser: {
serialize: ['!stdSerializers.err', 'custom'] //will not serialize Errors, will serialize `custom` keys
}
})
```
The `serialize` array also applies to any child logger serializers (see https://github.com/pinojs/pino/blob/master/docs/api.md#discussion-2
for how to set child-bound serializers).
Unlike server pino the serializers apply to every object passed to the logger method,
if the `asObject` option is `true`, this results in the serializers applying to the
first object (as in server pino).
For more info on serializers see https://github.com/pinojs/pino/blob/master/docs/api.md#mergingobject.
### `transmit` (Object)
An object with `send` and `level` properties.
The `transmit.level` property specifies the minimum level (inclusive) of when the `send` function
should be called, if not supplied the `send` function be called based on the main logging `level`
(set via `options.level`, defaulting to `info`).
The `transmit` object must have a `send` function which will be called after
writing the log message. The `send` function is passed the level of the log
message and a `logEvent` object.
The `logEvent` object is a data structure representing a log message, it represents
the arguments passed to a logger statement, the level
at which they were logged, and the hierarchy of child bindings.
The `logEvent` format is structured like so:
```js
{
ts = Number,
messages = Array,
bindings = Array,
level: { label = String, value = Number}
}
```
The `ts` property is a Unix epoch timestamp in milliseconds, the time is taken from the moment the
logger method is called.
The `messages` array is all arguments passed to logger method, (for instance `logger.info('a', 'b', 'c')`
would result in `messages` array `['a', 'b', 'c']`).
The `bindings` array represents each child logger (if any), and the relevant bindings.
For instance, given `logger.child({a: 1}).child({b: 2}).info({c: 3})`, the bindings array
would hold `[{a: 1}, {b: 2}]` and the `messages` array would be `[{c: 3}]`. The `bindings`
are ordered according to their position in the child logger hierarchy, with the lowest index
being the top of the hierarchy.
By default, serializers are not applied to log output in the browser, but they will *always* be
applied to `messages` and `bindings` in the `logEvent` object. This allows us to ensure a consistent
format for all values between server and client.
The `level` holds the label (for instance `info`), and the corresponding numerical value
(for instance `30`). This could be important in cases where client-side level values and
labels differ from server-side.
The point of the `send` function is to remotely record log messages:
```js
const pino = require('pino')({
browser: {
transmit: {
level: 'warn',
send: function (level, logEvent) {
if (level === 'warn') {
// maybe send the logEvent to a separate endpoint
// or maybe analyze the messages further before sending
}
// we could also use the `logEvent.level.value` property to determine
// numerical value
if (logEvent.level.value >= 50) { // covers error and fatal
// send the logEvent somewhere
}
}
}
}
})
```
### `disabled` (Boolean)
```js
const pino = require('pino')({browser: {disabled: true}})
```
The `disabled` option will disable logging in browser if set
to `true`, by default it is set to `false`.
================================================
FILE: docs/bundling.md
================================================
# Bundling
Due to its internal architecture based on Worker Threads, it is not possible to bundle Pino *without* generating additional files.
In particular, a bundler must ensure that the following files are also bundled separately:
* `lib/worker.js` from the `thread-stream` dependency
* `file.js`
* `lib/worker.js`
* Any transport used by the user (like `pino-pretty`)
Once the files above have been generated, the bundler must also add information about the files above by injecting a code that sets `__bundlerPathsOverrides` in the `globalThis` object.
The variable is an object whose keys are an identifier for the files and the values are the paths of files relative to the currently bundle files.
Example:
```javascript
// Inject this using your bundle plugin
globalThis.__bundlerPathsOverrides = {
'thread-stream-worker': pinoWebpackAbsolutePath('./thread-stream-worker.js')
'pino/file': pinoWebpackAbsolutePath('./pino-file.js'),
'pino-worker': pinoWebpackAbsolutePath('./pino-worker.js'),
'pino-pretty': pinoWebpackAbsolutePath('./pino-pretty.js'),
};
```
Note that `pino/file`, `pino-worker` and `thread-stream-worker` are required identifiers. Other identifiers are possible based on the user configuration.
## Webpack Plugin
If you are a Webpack user, you can achieve this with [pino-webpack-plugin](https://github.com/pinojs/pino-webpack-plugin) without manual configuration of `__bundlerPathsOverrides`; however, you still need to configure it manually if you are using other bundlers.
## Esbuild Plugin
[esbuild-plugin-pino](https://github.com/davipon/esbuild-plugin-pino) is the esbuild plugin to generate extra pino files for bundling.
## Bun Plugin
[bun-plugin-pino](https://github.com/vktrl/bun-plugin-pino) is the Bun plugin to generate extra pino files for bundling.
================================================
FILE: docs/child-loggers.md
================================================
# Child loggers
Let's assume we want to have `"module":"foo"` added to every log within a
module `foo.js`.
To accomplish this, simply use a child logger:
```js
'use strict'
// imports a pino logger instance of `require('pino')()`
const parentLogger = require('./lib/logger')
const log = parentLogger.child({module: 'foo'})
function doSomething () {
log.info('doSomething invoked')
}
module.exports = {
doSomething
}
```
## Cost of child logging
Child logger creation is fast:
```
benchBunyanCreation*10000: 564.514ms
benchBoleCreation*10000: 283.276ms
benchPinoCreation*10000: 258.745ms
benchPinoExtremeCreation*10000: 150.506ms
```
Logging through a child logger has little performance penalty:
```
benchBunyanChild*10000: 556.275ms
benchBoleChild*10000: 288.124ms
benchPinoChild*10000: 231.695ms
benchPinoExtremeChild*10000: 122.117ms
```
Logging via the child logger of a child logger also has negligible overhead:
```
benchBunyanChildChild*10000: 559.082ms
benchPinoChildChild*10000: 229.264ms
benchPinoExtremeChildChild*10000: 127.753ms
```
## Duplicate keys caveat
Naming conflicts can arise between child loggers and
children of child loggers.
This isn't as bad as it sounds, even if the same keys between
parent and child loggers are used, Pino resolves the conflict in the sanest way.
For example, consider the following:
```js
const pino = require('pino')
pino(pino.destination('./my-log'))
.child({a: 'property'})
.child({a: 'prop'})
.info('howdy')
```
```sh
$ cat my-log
{"pid":95469,"hostname":"MacBook-Pro-3.home","level":30,"msg":"howdy","time":1459534114473,"a":"property","a":"prop"}
```
Notice how there are two keys named `a` in the JSON output. The sub-child's properties
appear after the parent child properties.
At some point, the logs will most likely be processed (for instance with a [transport](transports.md)),
and this generally involves parsing. `JSON.parse` will return an object where the conflicting
namespace holds the final value assigned to it:
```sh
$ cat my-log | node -e "process.stdin.once('data', (line) => console.log(JSON.stringify(JSON.parse(line))))"
{"pid":95469,"hostname":"MacBook-Pro-3.home","level":30,"msg":"howdy","time":"2016-04-01T18:08:34.473Z","a":"prop"}
```
Ultimately the conflict is resolved by taking the last value, which aligns with Bunyan's child logging
behavior.
There may be cases where this edge case becomes problematic if a JSON parser with alternative behavior
is used to process the logs. It's recommended to be conscious of namespace conflicts with child loggers,
in light of an expected log processing approach.
One of Pino's performance tricks is to avoid building objects and stringifying
them, so we're building strings instead. This is why duplicate keys between
parents and children will end up in the log output.
================================================
FILE: docs/diagnostics.md
================================================
# Diagnostics
Pino provides [tracing channel](tc) events that allow insight into the
internal workings of the library. The currently supported events are:
+ `tracing:pino_asJson:start`: emitted when the final serialization process
of logs is started. The emitted event payload has the following fields:
- `instance`: the Pino instance associated with the function
- `arguments`: the arguments passed to the function
+ `tracing:pino_asJson:end`: emitted at the end of the final serialization
process. The emitted event payload has the following fields:
- `instance`: the Pino instance associated with the function
- `arguments`: the arguments passed to the function
- `result`: the finalized, newline delimited, log line as a string
[tc]: https://nodejs.org/docs/latest/api/diagnostics_channel.html#tracingchannel-channels
================================================
FILE: docs/ecosystem.md
================================================
# Pino Ecosystem
This is a list of ecosystem modules that integrate with `pino`.
Modules listed under [Core](#core) are maintained by the Pino team. Modules
listed under [Community](#community) are maintained by independent community
members.
Please send a PR to add new modules!
<a id="core"></a>
## Core
### Frameworks
+ [`express-pino-logger`](https://github.com/pinojs/express-pino-logger): use
Pino to log requests within [express](https://expressjs.com/).
+ [`koa-pino-logger`](https://github.com/pinojs/koa-pino-logger): use Pino to
log requests within [Koa](https://koajs.com/).
+ [`restify-pino-logger`](https://github.com/pinojs/restify-pino-logger): use
Pino to log requests within [restify](http://restify.com/).
+ [`rill-pino-logger`](https://github.com/pinojs/rill-pino-logger): use Pino as
the logger for the [Rill framework](https://rill.site/).
### Utilities
+ [`pino-arborsculpture`](https://github.com/pinojs/pino-arborsculpture): change
log levels at runtime.
+ [`pino-caller`](https://github.com/pinojs/pino-caller): add callsite to the log line.
+ [`pino-clf`](https://github.com/pinojs/pino-clf): reformat Pino logs into
Common Log Format.
+ [`pino-console`](https://github.com/pinojs/pino-console): adapter for the [WHATWG Console](https://console.spec.whatwg.org/) spec.
+ [`pino-debug`](https://github.com/pinojs/pino-debug): use Pino to interpret
[`debug`](https://npm.im/debug) logs.
+ [`pino-elasticsearch`](https://github.com/pinojs/pino-elasticsearch): send
Pino logs to an Elasticsearch instance.
+ [`pino-eventhub`](https://github.com/pinojs/pino-eventhub): send Pino logs
to an [Event Hub](https://docs.microsoft.com/en-us/azure/event-hubs/event-hubs-what-is-event-hubs).
+ [`pino-filter`](https://github.com/pinojs/pino-filter): filter Pino logs in
the same fashion as the [`debug`](https://npm.im/debug) module.
+ [`pino-gelf`](https://github.com/pinojs/pino-gelf): reformat Pino logs into
GELF format for Graylog.
+ [`pino-hapi`](https://github.com/pinojs/hapi-pino): use Pino as the logger
for [Hapi](https://hapijs.com/).
+ [`pino-http`](https://github.com/pinojs/pino-http): easily use Pino to log
requests with the core `http` module.
+ [`pino-http-print`](https://github.com/pinojs/pino-http-print): reformat Pino
logs into traditional [HTTPD](https://httpd.apache.org/) style request logs.
+ [`pino-mongodb`](https://github.com/pinojs/pino-mongodb): store Pino logs
in a MongoDB database.
+ [`pino-multi-stream`](https://github.com/pinojs/pino-multi-stream): send
logs to multiple destination streams (slow!).
+ [`pino-noir`](https://github.com/pinojs/pino-noir): redact sensitive information
in logs.
+ [`pino-pretty`](https://github.com/pinojs/pino-pretty): basic prettifier to
make log lines human-readable.
+ [`pino-socket`](https://github.com/pinojs/pino-socket): send logs to TCP or UDP
destinations.
+ [`pino-std-serializers`](https://github.com/pinojs/pino-std-serializers): the
core object serializers used within Pino.
+ [`pino-syslog`](https://github.com/pinojs/pino-syslog): reformat Pino logs
to standard syslog format.
+ [`pino-tee`](https://github.com/pinojs/pino-tee): pipe Pino logs into files
based upon log levels.
+ [`pino-test`](https://github.com/pinojs/pino-test): a set of utilities for
verifying logs generated by the Pino logger.
+ [`pino-toke`](https://github.com/pinojs/pino-toke): reformat Pino logs
according to a given format string.
<a id="community"></a>
## Community
+ [`@google-cloud/pino-logging-gcp-config`](https://www.npmjs.com/package/@google-cloud/pino-logging-gcp-config): Config helper and formatter to output [Google Cloud Platform Structured Logging](https://cloud.google.com/logging/docs/structured-logging)
+ [`@newrelic/pino-enricher`](https://github.com/newrelic/newrelic-node-log-extensions/blob/main/packages/pino-log-enricher): a log customization to add New Relic context to use [Logs In Context](https://docs.newrelic.com/docs/logs/logs-context/logs-in-context/)
+ [`@sentry/node`](https://docs.sentry.io/platforms/javascript/guides/node/configuration/integrations/pino/): Sentry SDK with native pinoIntegration for direct Pino instrumentation and error tracking.
+ [`cloud-pine`](https://github.com/metcoder95/cloud-pine): transport that provides abstraction and compatibility with [`@google-cloud/logging`](https://www.npmjs.com/package/@google-cloud/logging).
+ [`cls-proxify`](https://github.com/keenondrums/cls-proxify): integration of pino and [CLS](https://github.com/jeff-lewis/cls-hooked). Useful for creating dynamically configured child loggers (e.g. with added trace ID) for each request.
+ [`crawlee-pino`](https://github.com/imyelo/crawlee-pino): use Pino to log within Crawlee
+ [`eslint-plugin-pino`](https://github.com/orzarchi/eslint-plugin-pino): linting rules for pino usage, primarly for preventing missing context in logs due to incorrect argument order.
+ [`pino-colada`](https://github.com/lrlna/pino-colada): cute ndjson formatter for pino.
+ [`pino-dev`](https://github.com/dnjstrom/pino-dev): simple prettifier for pino with built-in support for common ecosystem packages.
+ [`pino-fluentd`](https://github.com/davidedantonio/pino-fluentd): send Pino logs to Elasticsearch,
MongoDB, and many [others](https://www.fluentd.org/dataoutputs) via Fluentd.
+ [`pino-lambda`](https://github.com/FormidableLabs/pino-lambda): log transport for cloudwatch support inside aws-lambda
+ [`pino-pretty-min`](https://github.com/unjello/pino-pretty-min): a minimal
prettifier inspired by the [logrus](https://github.com/sirupsen/logrus) logger.
+ [`pino-rotating-file`](https://github.com/homeaway/pino-rotating-file): a hapi-pino log transport for splitting logs into separate, automatically rotating files.
+ [`pino-tiny`](https://github.com/holmok/pino-tiny): a tiny (and extensible?) little log formatter for pino.
================================================
FILE: docs/help.md
================================================
# Help
* [Log rotation](#rotate)
* [Reopening log files](#reopening)
* [Saving to multiple files](#multiple)
* [Log filtering](#filter-logs)
* [Transports and systemd](#transport-systemd)
* [Log to different streams](#multi-stream)
* [Duplicate keys](#dupe-keys)
* [Log levels as labels instead of numbers](#level-string)
* [Pino with `debug`](#debug)
* [Unicode and Windows terminal](#windows)
* [Mapping Pino Log Levels to Google Cloud Logging (Stackdriver) Severity Levels](#stackdriver)
* [Using Grafana Loki to evaluate pino logs in a kubernetes cluster](#grafana-loki)
* [Avoid Message Conflict](#avoid-message-conflict)
* [Best performance for logging to `stdout`](#best-performance-for-stdout)
* [Testing](#testing)
<a id="rotate"></a>
## Log rotation
Use a separate tool for log rotation:
We recommend [logrotate](https://github.com/logrotate/logrotate).
Consider we output our logs to `/var/log/myapp.log` like so:
```
$ node server.js > /var/log/myapp.log
```
We would rotate our log files with logrotate, by adding the following to `/etc/logrotate.d/myapp`:
```
/var/log/myapp.log {
su root
daily
rotate 7
delaycompress
compress
notifempty
missingok
copytruncate
}
```
The `copytruncate` configuration has a very slight possibility of lost log lines due
to a gap between copying and truncating - the truncate may occur after additional lines
have been written. To perform log rotation without `copytruncate`, see the [Reopening log files](#reopening)
help.
<a id="reopening"></a>
## Reopening log files
In cases where a log rotation tool doesn't offer copy-truncate capabilities,
or where using them is deemed inappropriate, `pino.destination`
can reopen file paths after a file has been moved away.
One way to use this is to set up a `SIGUSR2` or `SIGHUP` signal handler that
reopens the log file destination, making sure to write the process PID out
somewhere so the log rotation tool knows where to send the signal.
```js
// write the process pid to a well known location for later
const fs = require('node:fs')
fs.writeFileSync('/var/run/myapp.pid', process.pid)
const dest = pino.destination('/log/file')
const logger = require('pino')(dest)
process.on('SIGHUP', () => dest.reopen())
```
The log rotation tool can then be configured to send this signal to the process
after a log rotation event has occurred.
Given a similar scenario as in the [Log rotation](#rotate) section a basic
`logrotate` config that aligns with this strategy would look similar to the following:
```
/var/log/myapp.log {
su root
daily
rotate 7
delaycompress
compress
notifempty
missingok
postrotate
kill -HUP `cat /var/run/myapp.pid`
endscript
}
```
<a id="multiple"></a>
## Saving to multiple files
See [`pino.multistream`](/docs/api.md#pino-multistream).
<a id="filter-logs"></a>
## Log Filtering
The Pino philosophy advocates common, preexisting, system utilities.
Some recommendations in line with this philosophy are:
1. Use [`grep`](https://linux.die.net/man/1/grep):
```sh
$ # View all "INFO" level logs
$ node app.js | grep '"level":30'
```
1. Use [`jq`](https://stedolan.github.io/jq/):
```sh
$ # View all "ERROR" level logs
$ node app.js | jq 'select(.level == 50)'
```
<a id="transport-systemd"></a>
## Transports and systemd
`systemd` makes it complicated to use pipes in services. One method for overcoming
this challenge is to use a subshell:
```
ExecStart=/bin/sh -c '/path/to/node app.js | pino-transport'
```
<a id="multi-stream"></a>
## Log to different streams
Pino's default log destination is the singular destination of `stdout`. While
not recommended for performance reasons, multiple destinations can be targeted
by using [`pino.multistream`](/docs/api.md#pino-multistream).
In this example, we use `stderr` for `error` level logs and `stdout` as default
for all other levels (e.g. `debug`, `info`, and `warn`).
```js
const pino = require('pino')
var streams = [
{level: 'debug', stream: process.stdout},
{level: 'error', stream: process.stderr},
{level: 'fatal', stream: process.stderr}
]
const logger = pino({
name: 'my-app',
level: 'debug', // must be the lowest level of all streams
}, pino.multistream(streams))
```
<a id="dupe-keys"></a>
## How Pino handles duplicate keys
Duplicate keys are possibly when a child logger logs an object with a key that
collides with a key in the child loggers bindings.
See the [child logger duplicate keys caveat](/docs/child-loggers.md#duplicate-keys-caveat)
for information on this is handled.
<a id="level-string"></a>
## Log levels as labels instead of numbers
Pino log lines are meant to be parsable. Thus, Pino's default mode of operation
is to print the level value instead of the string name.
However, you can use the [`formatters`](/docs/api.md#formatters-object) option
with a [`level`](/docs/api.md#level) function to print the string name instead of the level value :
```js
const pino = require('pino')
const log = pino({
formatters: {
level: (label) => {
return {
level: label
}
}
}
})
log.info('message')
// {"level":"info","time":1661632832200,"pid":18188,"hostname":"foo","msg":"message"}
```
Although it works, we recommend using one of these options instead if you are able:
1. If the only change desired is the name then a transport can be used. One such
transport is [`pino-text-level-transport`](https://npm.im/pino-text-level-transport).
1. Use a prettifier like [`pino-pretty`](https://npm.im/pino-pretty) to make
the logs human friendly.
<a id="debug"></a>
## Pino with `debug`
The popular [`debug`](https://npm.im/debug) is used in many modules across the ecosystem.
The [`pino-debug`](https://github.com/pinojs/pino-debug) module
can capture calls to `debug` loggers and run them
through `pino` instead. This results in a 10x (20x in asynchronous mode)
performance improvement - even though `pino-debug` is logging additional
data and wrapping it in JSON.
To quickly enable this install [`pino-debug`](https://github.com/pinojs/pino-debug)
and preload it with the `-r` flag, enabling any `debug` logs with the
`DEBUG` environment variable:
```sh
$ npm i pino-debug
$ DEBUG=* node -r pino-debug app.js
```
[`pino-debug`](https://github.com/pinojs/pino-debug) also offers fine-grain control to map specific `debug`
namespaces to `pino` log levels. See [`pino-debug`](https://github.com/pinojs/pino-debug)
for more.
<a id="windows"></a>
## Unicode and Windows terminal
Pino uses [sonic-boom](https://github.com/mcollina/sonic-boom) to speed
up logging. Internally, it uses [`fs.write`](https://nodejs.org/dist/latest-v10.x/docs/api/fs.html#fs_fs_write_fd_string_position_encoding_callback) to write log lines directly to a file
descriptor. On Windows, Unicode output is not handled properly in the
terminal (both `cmd.exe` and PowerShell), and as such the output could
be visualized incorrectly if the log lines include utf8 characters. It
is possible to configure the terminal to visualize those characters
correctly with the use of [`chcp`](https://ss64.com/nt/chcp.html) by
executing in the terminal `chcp 65001`. This is a known limitation of
Node.js.
<a id="stackdriver"></a>
## Mapping Pino Log Levels to Google Cloud Logging (Stackdriver) Severity Levels
Google Cloud Logging uses `severity` levels instead of log levels. As a result, all logs may show as INFO
level logs while completely ignoring the level set in the pino log. Google Cloud Logging also prefers that
log data is present inside a `message` key instead of the default `msg` key that Pino uses. Use a technique
similar to the one below to retain log levels in Google Cloud Logging
```js
const pino = require('pino')
// https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#logseverity
const PinoLevelToSeverityLookup = {
trace: 'DEBUG',
debug: 'DEBUG',
info: 'INFO',
warn: 'WARNING',
error: 'ERROR',
fatal: 'CRITICAL',
};
const defaultPinoConf = {
messageKey: 'message',
formatters: {
level(label, number) {
return {
severity: PinoLevelToSeverityLookup[label] || PinoLevelToSeverityLookup['info'],
level: number,
}
}
},
}
module.exports = function createLogger(options) {
return pino(Object.assign({}, options, defaultPinoConf))
}
```
A library that configures Pino for
[Google Cloud Structured Logging](https://cloud.google.com/logging/docs/structured-logging)
is available at:
[@google-cloud/pino-logging-gcp-config](https://www.npmjs.com/package/@google-cloud/pino-logging-gcp-config)
This library has the following features:
+ Converts Pino log levels to Google Cloud Logging log levels, as above
+ Uses `message` instead of `msg` for the message key, as above
+ Adds a millisecond-granularity timestamp in the
[structure](https://cloud.google.com/logging/docs/agent/logging/configuration#timestamp-processing)
recognised by Google Cloud Logging eg: \
`"timestamp":{"seconds":1445470140,"nanos":123000000}`
+ Adds a sequential
[`insertId`](https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#FIELDS.insert_id)
to ensure log messages with identical timestamps are ordered correctly.
+ Logs including an `Error` object have the
[`stack_trace`](https://cloud.google.com/error-reporting/docs/formatting-error-messages#log-error)
property set so that the error is forwarded to Google Cloud Error Reporting.
+ Includes a
[`ServiceContext`](https://cloud.google.com/error-reporting/reference/rest/v1beta1/ServiceContext)
object in the logs for Google Cloud Error Reporting, auto detected from the
environment if not specified
+ Maps the OpenTelemetry properties `span_id`, `trace_id`, and `trace_flags`
to the equivalent Google Cloud Logging fields.
<a id="grafana-loki"></a>
## Using Grafana Loki to evaluate pino logs in a kubernetes cluster
To get pino logs into Grafana Loki there are two options:
1. **Push:** Use [pino-loki](https://github.com/Julien-R44/pino-loki) to send logs directly to Loki.
1. **Pull:** Configure Grafana Promtail to read and properly parse the logs before sending them to Loki.
Similar to Google Cloud logging, this involves remapping the log levels. See this [article](https://medium.com/@janpaepke/structured-logging-in-the-grafana-monitoring-stack-8aff0a5af2f5) for details.
<a id="avoid-message-conflict"></a>
## Avoid Message Conflict
As described in the [`message` documentation](/docs/api.md#message), when a log
is written like `log.info({ msg: 'a message' }, 'another message')` then the
final output JSON will have `"msg":"another message"` and the `'a message'`
string will be lost. To overcome this, the [`logMethod` hook](/docs/api.md#logmethod)
can be used:
```js
'use strict'
const log = require('pino')({
level: 'debug',
hooks: {
logMethod (inputArgs, method) {
if (inputArgs.length === 2 && inputArgs[0].msg) {
inputArgs[0].originalMsg = inputArgs[0].msg
}
return method.apply(this, inputArgs)
}
}
})
log.info('no original message')
log.info({ msg: 'mapped to originalMsg' }, 'a message')
// {"level":30,"time":1596313323106,"pid":63739,"hostname":"foo","msg":"no original message"}
// {"level":30,"time":1596313323107,"pid":63739,"hostname":"foo","msg":"a message","originalMsg":"mapped to originalMsg"}
```
<a id="best-performance-for-stdout"></a>
## Best performance for logging to `stdout`
The best performance for logging directly to stdout is _usually_ achieved by using the
default configuration:
```js
const log = require('pino')();
```
You should only have to configure custom transports or other settings
if you have broader logging requirements.
<a id="testing"></a>
## Testing
See [`pino-test`](https://github.com/pinojs/pino-test).
================================================
FILE: docs/lts.md
================================================
## Long Term Support
Pino's Long Term Support (LTS) is provided according to the schedule laid
out in this document:
1. Major releases, "X" release of [semantic versioning][semver] X.Y.Z release
versions, are supported for a minimum period of six months from their release
date. The release date of any specific version can be found at
[https://github.com/pinojs/pino/releases](https://github.com/pinojs/pino/releases).
1. Major releases will receive security updates for an additional six months
from the release of the next major release. After this period
we will still review and release security fixes as long as they are
provided by the community and they do not violate other constraints,
e.g. minimum supported Node.js version.
1. Major releases will be tested and verified against all Node.js
release lines that are supported by the
[Node.js LTS policy](https://github.com/nodejs/Release) within the
LTS period of that given Pino release line. This implies that only
the latest Node.js release of a given line is supported.
A "month" is defined as 30 consecutive days.
> ## Security Releases and Semver
>
> As a consequence of providing long-term support for major releases, there
> are occasions where we need to release breaking changes as a _minor_
> version release. Such changes will _always_ be noted in the
> [release notes](https://github.com/pinojs/pino/releases).
>
> To avoid automatically receiving breaking security updates it is possible to use
> the tilde (`~`) range qualifier. For example, to get patches for the 6.1
> release, and avoid automatically updating to the 6.1 release, specify
> the dependency as `"pino": "~6.1.x"`. This will leave your application vulnerable,
> so please use with caution.
[semver]: https://semver.org/
<a name="lts-schedule"></a>
### Schedule
| Version | Release Date | End Of LTS Date | Node.js |
| :------ | :----------- | :-------------- | :------------------- |
| 9.x | 2024-04-26 | TBD | 18, 20, 22 |
| 8.x | 2022-06-01 | 2024-10-26 | 14, 16, 18, 20 |
| 7.x | 2021-10-14 | 2023-06-01 | 12, 14, 16 |
| 6.x | 2020-03-07 | 2022-04-14 | 10, 12, 14, 16 |
<a name="supported-os"></a>
### CI tested operating systems
Pino uses GitHub Actions for CI testing, please refer to
[GitHub's documentation regarding workflow runners](https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources)
for further details on what the latest virtual environment is in relation to
the YAML workflow labels below:
| OS | YAML Workflow Label | Node.js |
|---------|------------------------|--------------|
| Linux | `ubuntu-latest` | 18, 20, 22 |
| Windows | `windows-latest` | 18, 20, 22 |
| MacOS | `macos-latest` | 18, 20, 22 |
================================================
FILE: docs/pretty.md
================================================
# Pretty Printing
By default, Pino log lines are newline delimited JSON (NDJSON). This is perfect
for production usage and long-term storage. It's not so great for development
environments. Thus, Pino logs can be prettified by using a Pino prettifier
module like [`pino-pretty`][pp]:
1. Install a prettifier module as a separate dependency, e.g. `npm install pino-pretty`.
2. Instantiate the logger with the `transport.target` option set to `'pino-pretty'`:
```js
const pino = require('pino')
const logger = pino({
transport: {
target: 'pino-pretty'
},
})
logger.info('hi')
```
3. The transport option can also have an options object containing `pino-pretty` options:
```js
const pino = require('pino')
const logger = pino({
transport: {
target: 'pino-pretty',
options: {
colorize: true
}
}
})
logger.info('hi')
```
**Note:** The `logger.flush()` method does not work when using `pino-pretty`. See
[Flush Limitations with `pino-pretty`](/docs/asynchronous.md) for more details.
[pp]: https://github.com/pinojs/pino-pretty
================================================
FILE: docs/redaction.md
================================================
# Redaction
To redact sensitive information, supply paths to keys that hold sensitive data
using the `redact` option. Note that paths that contain hyphens need to use
brackets to access the hyphenated property:
```js
const logger = require('.')({\n redact: ['key', 'path.to.key', 'stuff.thats[*].secret', 'path["with-hyphen"]']
})
logger.info({
key: 'will be redacted',
path: {
to: {key: 'sensitive', another: 'thing'}
},
stuff: {
thats: [
{secret: 'will be redacted', logme: 'will be logged'},
{secret: 'as will this', logme: 'as will this'}
]
}
})
```
This will output:
```JSON
{"level":30,"time":1527777350011,"pid":3186,"hostname":"Davids-MacBook-Pro-3.local","key":"[Redacted]","path":{"to":{"key":"[Redacted]","another":"thing"}},"stuff":{"thats":[{"secret":"[Redacted]","logme":"will be logged"},{"secret":"[Redacted]","logme":"as will this"}]}}
```
The `redact` option can take an array (as shown in the above example) or
an object. This allows control over *how* information is redacted.
For instance, setting the censor:
```js
const logger = require('.')({\n redact: {
paths: ['key', 'path.to.key', 'stuff.thats[*].secret'],
censor: '**GDPR COMPLIANT**'
}
})
logger.info({
key: 'will be redacted',
path: {
to: {key: 'sensitive', another: 'thing'}
},
stuff: {
thats: [
{secret: 'will be redacted', logme: 'will be logged'},
{secret: 'as will this', logme: 'as will this'}
]
}
})
```
This will output:
```JSON
{"level":30,"time":1527778563934,"pid":3847,"hostname":"Davids-MacBook-Pro-3.local","key":"**GDPR COMPLIANT**","path":{"to":{"key":"**GDPR COMPLIANT**","another":"thing"}},"stuff":{"thats":[{"secret":"**GDPR COMPLIANT**","logme":"will be logged"},{"secret":"**GDPR COMPLIANT**","logme":"as will this"}]}}
```
The `redact.remove` option also allows for the key and value to be removed from output:
```js
const logger = require('.')({\n redact: {
paths: ['key', 'path.to.key', 'stuff.thats[*].secret'],
remove: true
}
})
logger.info({
key: 'will be redacted',
path: {
to: {key: 'sensitive', another: 'thing'}
},
stuff: {
thats: [
{secret: 'will be redacted', logme: 'will be logged'},
{secret: 'as will this', logme: 'as will this'}
]
}
})
```
This will output
```JSON
{"level":30,"time":1527782356751,"pid":5758,"hostname":"Davids-MacBook-Pro-3.local","path":{"to":{"another":"thing"}},"stuff":{"thats":[{"logme":"will be logged"},{"logme":"as will this"}]}}
```
See [pino options in API](/docs/api.md#redact-array-object) for `redact` API details.
<a name="paths"></a>
## Path Syntax
The syntax for paths supplied to the `redact` option conform to the syntax in path lookups
in standard ECMAScript, with two additions:
* paths may start with bracket notation
* paths may contain the asterisk `*` to denote a wildcard
* paths are **case sensitive**
By way of example, the following are all valid paths:
* `a.b.c`
* `a["b-c"].d`
* `["a-b"].c`
* `a.b.*`
* `a[*].b`
## Overhead
Pino's redaction functionality is built on top of [`fast-redact`](https://github.com/davidmarkclements/fast-redact)
which adds about 2% overhead to `JSON.stringify` when using paths without wildcards.
When used with pino logger with a single redacted path, any overhead is within noise -
a way to deterministically measure its effect has not been found. This is because it is not a bottleneck.
However, wildcard redaction does carry a non-trivial cost relative to explicitly declaring the keys
(50% in a case where four keys are redacted across two objects). See
the [`fast-redact` benchmarks](https://github.com/davidmarkclements/fast-redact#benchmarks) for details.
## Safety
The `redact` option is intended as an initialization time configuration option.
Path strings must not originate from user input.
The `fast-redact` module uses a VM context to syntax check the paths, user input
should never be combined with such an approach. See the [`fast-redact` Caveat](https://github.com/davidmarkclements/fast-redact#caveat)
and the [`fast-redact` Approach](https://github.com/davidmarkclements/fast-redact#approach) for in-depth information.
================================================
FILE: docs/transports.md
================================================
# Transports
Pino transports can be used for both transmitting and transforming log output.
The way Pino generates logs:
1. Reduces the impact of logging on an application to the absolute minimum.
2. Gives greater flexibility in how logs are processed and stored.
It is recommended that any log transformation or transmission is performed either
in a separate thread or a separate process.
Before Pino v7 transports would ideally operate in a separate process - these are
now referred to as [Legacy Transports](#legacy-transports).
From Pino v7 and upwards transports can also operate inside a [Worker Thread][worker-thread]
and can be used or configured via the options object passed to `pino` on initialization.
In this case the transports would always operate asynchronously (unless `options.sync` is set to `true` in transport options), and logs would be
flushed as quickly as possible (there is nothing to do).
[worker-thread]: https://nodejs.org/dist/latest-v14.x/docs/api/worker_threads.html
## v7+ Transports
A transport is a module that exports a default function that returns a writable stream:
```js
import { createWriteStream } from 'node:fs'
export default (options) => {
return createWriteStream(options.destination)
}
```
Let's imagine the above defines our "transport" as the file `my-transport.mjs`
(ESM files are supported even if the project is written in CJS).
We would set up our transport by creating a transport stream with `pino.transport`
and passing it to the `pino` function:
```js
const pino = require('pino')
const transport = pino.transport({
target: '/absolute/path/to/my-transport.mjs'
})
pino(transport)
```
The transport code will be executed in a separate worker thread. The main thread
will write logs to the worker thread, which will write them to the stream returned
from the function exported from the transport file/module.
The exported function can also be async. If we use an async function we can throw early
if the transform could not be opened. As an example:
```js
import fs from 'node:fs'
import { once } from 'events'
export default async (options) => {
const stream = fs.createWriteStream(options.destination)
await once(stream, 'open')
return stream
}
```
While initializing the stream we're able to use `await` to perform asynchronous operations. In this
case, waiting for the write streams `open` event.
Let's imagine the above was published to npm with the module name `some-file-transport`.
The `options.destination` value can be set when creating the transport stream with `pino.transport` like so:
```js
const pino = require('pino')
const transport = pino.transport({
target: 'some-file-transport',
options: { destination: '/dev/null' }
})
pino(transport)
```
Note here we've specified a module by package rather than by relative path. The options object we provide
is serialized and injected into the transport worker thread, then passed to the module's exported function.
This means that the options object can only contain types that are supported by the
[Structured Clone Algorithm][sca] which is used to (de)serialize objects between threads.
What if we wanted to use both transports, but send only error logs to `my-transport.mjs` while
sending all logs to `some-file-transport`? We can use the per-target `level` option:
```js
const pino = require('pino')
const transport = pino.transport({
targets: [
{ target: '/absolute/path/to/my-transport.mjs', level: 'error' },
{ target: 'some-file-transport', options: { destination: '/dev/null' }}
]
})
pino(transport)
```
### How level filtering works with transports
Pino applies level filtering in two different places depending on the transport configuration.
#### A) Single `target` (or single `pipeline`)
```text
Main thread Worker thread
──────────────────────────────────────────── ─────────────────────────────────
logger.debug()/info()/... transport target (stream)
│ ▲
▼ │
logger.level gate (enabled methods) ──ThreadStream─────┘
Result:
- `logger.level` decides what is emitted.
- `transport.level` is not used in this path.
```
#### B) `targets` (multiple destinations)
```text
Main thread Worker thread
──────────────────────────────────────────── ─────────────────────────────────
logger.debug()/info()/... pino.multistream
│ (per-target level filter)
▼ │
logger.level gate (enabled methods) ──ThreadStream────┼──> target #1 (level: ...)
├──> target #2 (level: ...)
└──> target #N (default: info)
Result:
- `logger.level` is the first gate.
- each `targets[i].level` is the second gate.
- missing `targets[i].level` defaults to `info`.
```
Summary:
- Single `target`/single `pipeline`: only `logger.level` filters messages.
- Multiple `targets`: `logger.level` is applied first, then each `targets[i].level` is applied.
- If `targets[i].level` is missing, it defaults to `info`.
If you need `debug` (or lower) logs to reach one or more targets, set:
1. `logger.level` low enough, and
2. `level` on each target that should receive those messages.
If we're using custom levels, they should be passed in when using more than one transport.
```js
const pino = require('pino')
const transport = pino.transport({
targets: [
{ target: '/absolute/path/to/my-transport.mjs', level: 'error' },
{ target: 'some-file-transport', options: { destination: '/dev/null' }
],
levels: { foo: 35 }
})
pino(transport)
```
It is also possible to use the `dedupe` option to send logs only to the stream with the higher level.
```js
const pino = require('pino')
const transport = pino.transport({
targets: [
{ target: '/absolute/path/to/my-transport.mjs', level: 'error' },
{ target: 'some-file-transport', options: { destination: '/dev/null' }
],
dedupe: true
})
pino(transport)
```
To make pino log synchronously, pass `sync: true` to transport options.
```js
const pino = require('pino')
const transport = pino.transport({
targets: [
{ target: '/absolute/path/to/my-transport.mjs', level: 'error' },
],
dedupe: true,
sync: true,
});
pino(transport);
```
For more details on `pino.transport` see the [API docs for `pino.transport`][pino-transport].
### Using transports with `--import` or `--require` preloads
Pino transports work correctly when loaded via Node.js preload flags (`--import` or `--require`).
Pino automatically detects when it's being loaded during the preload phase and filters out the
preload flags from the transport worker's `execArgv` to prevent infinite worker spawning.
```js
// preload.mjs
import pino from 'pino'
export const logger = pino({
transport: {
target: 'pino-pretty'
}
})
```
```bash
node --import=./preload.mjs app.js
```
[pino-transport]: /docs/api.md#pino-transport
[sca]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm
<a id="writing"></a>
### Writing a Transport
The module [pino-abstract-transport](https://github.com/pinojs/pino-abstract-transport) provides
a simple utility to parse each line. Its usage is highly recommended.
You can see an example using an async iterator with ESM:
```js
import build from 'pino-abstract-transport'
import SonicBoom from 'sonic-boom'
import { once } from 'events'
export default async function (opts) {
// SonicBoom is necessary to avoid loops with the main thread.
// It is the same of pino.destination().
const destination = new SonicBoom({ dest: opts.destination || 1, sync: false })
await once(destination, 'ready')
return build(async function (source) {
for await (let obj of source) {
const toDrain = !destination.write(obj.msg.toUpperCase() + '\n')
// This block will handle backpressure
if (toDrain) {
await once(destination, 'drain')
}
}
}, {
async close (err) {
destination.end()
await once(destination, 'close')
}
})
}
```
or using Node.js streams and CommonJS:
```js
'use strict'
const build = require('pino-abstract-transport')
const SonicBoom = require('sonic-boom')
module.exports = function (opts) {
const destination = new SonicBoom({ dest: opts.destination || 1, sync: false })
return build(function (source) {
source.pipe(destination)
}, {
close (err, cb) {
destination.end()
destination.on('close', cb.bind(null, err))
}
})
}
```
(It is possible to use the async iterators with CommonJS and streams with ESM.)
To consume async iterators in batches, consider using the [hwp](https://github.com/mcollina/hwp) library.
The `close()` function is needed to make sure that the stream is closed and flushed when its
callback is called or the returned promise resolves. Otherwise, log lines will be lost.
### Writing to a custom transport & stdout
In case you want to both use a custom transport, and output the log entries with default processing to STDOUT, you can use 'pino/file' transport configured with `destination: 1`:
```js
const transports = [
{
target: 'pino/file',
options: { destination: 1 } // this writes to STDOUT
},
{
target: 'my-custom-transport',
options: { someParameter: true }
}
]
const logger = pino(pino.transport({ targets: transports }))
```
### Creating a transport pipeline
As an example, the following transport returns a `Transform` stream:
```js
import build from 'pino-abstract-transport'
import { pipeline, Transform } from 'node:stream'
export default async function (options) {
return build(function (source) {
const myTransportStream = new Transform({
// Make sure autoDestroy is set,
// this is needed in Node v12 or when using the
// readable-stream module.
autoDestroy: true,
objectMode: true,
transform (chunk, enc, cb) {
// modifies the payload somehow
chunk.service = 'pino'
// stringify the payload again
this.push(`${JSON.stringify(chunk)}\n`)
cb()
}
})
pipeline(source, myTransportStream, () => {})
return myTransportStream
}, {
// This is needed to be able to pipeline transports.
enablePipelining: true
})
}
```
Then you can pipeline them with:
```js
import pino from 'pino'
const logger = pino({
transport: {
pipeline: [{
target: './my-transform.js'
}, {
// Use target: 'pino/file' with STDOUT descriptor 1 to write
// logs without any change.
target: 'pino/file',
options: { destination: 1 }
}]
}
})
logger.info('hello world')
```
__NOTE: there is no "default" destination for a pipeline but
a terminating target, i.e. a `Writable` stream.__
### TypeScript compatibility
Pino provides support for transports written in TypeScript.
#### Node.js 22+ with Type Stripping
Starting with Node.js 22.6.0, you can use TypeScript transports directly with native type stripping support. This provides full ESM support without any transpilation or additional tooling:
```ts
// my-transport.mts
import { createWriteStream } from 'node:fs'
export default (options: { destination: string }) => {
return createWriteStream(options.destination)
}
```
```js
// app.js
const pino = require('pino')
const transport = pino.transport({
target: './my-transport.mts',
options: { destination: '/path/to/file' }
})
pino(transport)
```
**Version requirements:**
- **Node.js 22.6.0 - 22.17.x**: Use the `--experimental-strip-types` flag:
```sh
node --experimental-strip-types app.ts
```
- **Node.js 22.18.0+ and 24.0.0+**: Type stripping is enabled by default, no flag needed:
```sh
node app.ts
```
**Important notes:**
- Use the `.mts` extension for TypeScript ESM modules to ensure proper module resolution
- Alternatively, you can use `.ts` extension if your `package.json` has `"type": "module"`, but this requires your entire application to use ESM
- Type stripping is not available in Node.js 20.x or earlier versions
- This approach provides the cleanest TypeScript experience with full ESM support and no build step required
#### Using TS-Node (Legacy)
For older Node.js versions, you can use tools such as [TS-Node](https://typestrong.org/ts-node/) to execute TypeScript
code without explicit transpilation, but there are some known caveats:
- For "pure" TypeScript code, ES imports are not fully supported (ES imports are
supported once the code is transpiled).
- Only TS-Node is supported. Other TypeScript loaders like [TSM](https://github.com/lukeed/tsm) are not currently supported.
- Running transports TypeScript code on TS-Node may be problematic on
Windows systems.
#### Transpiled TypeScript (Recommended for Production)
For maximum compatibility and production use, it's still recommended to transpile
TypeScript transports to JavaScript before deployment.
### Notable transports
#### `pino/file`
The `pino/file` transport routes logs to a file (or file descriptor).
The `options.destination` property may be set to specify the desired file destination.
```js
const pino = require('pino')
const transport = pino.transport({
target: 'pino/file',
options: { destination: '/path/to/file' }
})
pino(transport)
```
By default, the `pino/file` transport assumes the directory of the destination file exists. If it does not exist, the transport will throw an error when it attempts to open the file for writing. The `mkdir` option may be set to `true` to configure the transport to create the directory, if it does not exist, before opening the file for writing.
```js
const pino = require('pino')
const transport = pino.transport({
target: 'pino/file',
options: { destination: '/path/to/file', mkdir: true }
})
pino(transport)
```
By default, the `pino/file` transport appends to the destination file if it exists. The `append` option may be set to `false` to configure the transport to truncate the file upon opening it for writing.
```js
const pino = require('pino')
const transport = pino.transport({
target: 'pino/file',
options: { destination: '/path/to/file', append: false }
})
pino(transport)
```
The `options.destination` property may also be a number to represent a file descriptor. Typically this would be `1` to write to STDOUT or `2` to write to STDERR. If `options.destination` is not set, it defaults to `1` which means logs will be written to STDOUT. If `options.destination` is a string integer, e.g. `'1'`, it will be coerced to a number and used as a file descriptor. If this is not desired, provide a full path, e.g. `/tmp/1`.
The difference between using the `pino/file` transport builtin and using `pino.destination` is that `pino.destination` runs in the main thread, whereas `pino/file` sets up `pino.destination` in a worker thread.
#### `pino-pretty`
The [`pino-pretty`][pino-pretty] transport prettifies logs.
By default the `pino-pretty` builtin logs to STDOUT.
The `options.destination` property may be set to log pretty logs to a file descriptor or file. The following would send the prettified logs to STDERR:
```js
const pino = require('pino')
const transport = pino.transport({
target: 'pino-pretty',
options: { destination: 1 } // use 2 for stderr
})
pino(transport)
```
### Asynchronous startup
The new transports boot asynchronously and calling `process.exit()` before the transport
starts will cause logs to not be delivered.
```js
const pino = require('pino')
const transport = pino.transport({
targets: [
{ target: '/absolute/path/to/my-transport.mjs', level: 'error' },
{ target: 'some-file-transport', options: { destination: '/dev/null' } }
]
})
const logger = pino(transport)
logger.info('hello')
// If logs are printed before the transport is ready when process.exit(0) is called,
// they will be lost.
transport.on('ready', function () {
process.exit(0)
})
```
## Legacy Transports
A legacy Pino "transport" is a supplementary tool that consumes Pino logs.
Consider the following example for creating a transport:
```js
const { pipeline, Writable } = require('node:stream')
const split = require('split2')
const myTransportStream = new Writable({
write (chunk, enc, cb) {
// apply a transform and send to STDOUT
console.log(chunk.toString().toUpperCase())
cb()
}
})
pipeline(process.stdin, split(JSON.parse), myTransportStream)
```
The above defines our "transport" as the file `my-transport-process.js`.
Logs can now be consumed using shell piping:
```sh
node my-app-which-logs-stuff-to-stdout.js | node my-transport-process.js
```
Ideally, a transport should consume logs in a separate process to the application,
Using transports in the same process causes unnecessary load and slows down
Node's single-threaded event loop.
## Known Transports
PRs to this document are welcome for any new transports!
### Pino v7+ Compatible
+ [@axiomhq/pino](#@axiomhq/pino)
+ [@logtail/pino](#@logtail/pino)
+ [@macfja/pino-fingers-crossed](#macfja-pino-fingers-crossed)
+ [@openobserve/pino-openobserve](#pino-openobserve)
+ [datadog-logger-integrations](#datadog-logger-integrations)
+ [pino-airbrake-transport](#pino-airbrake-transport)
+ [pino-axiom](#pino-axiom)
+ [pino-discord-webhook](#pino-discord-webhook)
+ [pino-elasticsearch](#pino-elasticsearch)
+ [pino-hana](#pino-hana)
+ [pino-logflare](#pino-logflare)
+ [pino-logfmt](#pino-logfmt)
+ [pino-loki](#pino-loki)
+ [pino-opentelemetry-transport](#pino-opentelemetry-transport)
+ [pino-pretty](#pino-pretty)
+ [pino-roll](#pino-roll)
+ [pino-seq-transport](#pino-seq-transport)
+ [pino-sentry-transport](#pino-sentry-transport)
+ [Sentry Native SDK Integration](#sentry-native-integration)
+ [pino-slack-webhook](#pino-slack-webhook)
+ [pino-telegram-webhook](#pino-telegram-webhook)
+ [pino-yc-transport](#pino-yc-transport)
### Legacy
+ [pino-applica
gitextract_1hdwcln0/ ├── .github/ │ ├── dependabot.yml │ └── workflows/ │ ├── bench.yml │ ├── ci.yml │ ├── lock-threads.yml │ ├── publish-release.yml │ └── target-main.yml ├── .gitignore ├── .nojekyll ├── .npmignore ├── .npmrc ├── .prettierignore ├── CNAME ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── benchmarks/ │ ├── basic.bench.js │ ├── child-child.bench.js │ ├── child-creation.bench.js │ ├── child.bench.js │ ├── deep-object.bench.js │ ├── formatters.bench.js │ ├── internal/ │ │ ├── custom-levels.js │ │ ├── just-pino-heavy.bench.js │ │ ├── just-pino.bench.js │ │ ├── parent-vs-child.bench.js │ │ └── redact.bench.js │ ├── long-string.bench.js │ ├── multi-arg.bench.js │ ├── multistream.js │ ├── object.bench.js │ └── utils/ │ ├── generate-benchmark-doc.js │ ├── runbench.js │ └── wrap-log-level.js ├── bin.js ├── browser.js ├── build/ │ └── sync-version.js ├── docs/ │ ├── api.md │ ├── asynchronous.md │ ├── benchmarks.md │ ├── browser.md │ ├── bundling.md │ ├── child-loggers.md │ ├── diagnostics.md │ ├── ecosystem.md │ ├── help.md │ ├── lts.md │ ├── pretty.md │ ├── redaction.md │ ├── transports.md │ └── web.md ├── docsify/ │ └── sidebar.md ├── eslint.config.js ├── examples/ │ ├── basic.js │ └── transport.js ├── file.js ├── inc-version.sh ├── index.html ├── lib/ │ ├── caller.js │ ├── constants.js │ ├── deprecations.js │ ├── levels.js │ ├── meta.js │ ├── multistream.js │ ├── proto.js │ ├── redaction.js │ ├── symbols.js │ ├── time.js │ ├── tools.js │ ├── transport-stream.js │ ├── transport.js │ └── worker.js ├── package.json ├── pino.d.ts ├── pino.js ├── test/ │ ├── basic.test.js │ ├── broken-pipe.test.js │ ├── browser-child.test.js │ ├── browser-disabled.test.js │ ├── browser-early-console-freeze.test.js │ ├── browser-is-level-enabled.test.js │ ├── browser-levels.test.js │ ├── browser-redaction.test.js │ ├── browser-serializers.test.js │ ├── browser-timestamp.test.js │ ├── browser-transmit.test.js │ ├── browser.test.js │ ├── complex-objects.test.js │ ├── crlf.test.js │ ├── custom-levels.test.js │ ├── diagnostics.test.js │ ├── error-key.test.js │ ├── error.test.js │ ├── escaping.test.js │ ├── esm/ │ │ ├── esm.mjs │ │ ├── index.test.js │ │ └── named-exports.mjs │ ├── exit.test.js │ ├── fixtures/ │ │ ├── broken-pipe/ │ │ │ ├── basic.js │ │ │ ├── destination.js │ │ │ └── syncfalse.js │ │ ├── console-transport.js │ │ ├── crashing-transport.js │ │ ├── default-exit.js │ │ ├── destination-exit.js │ │ ├── eval/ │ │ │ ├── index.js │ │ │ └── node_modules/ │ │ │ ├── 14-files.js │ │ │ ├── 2-files.js │ │ │ ├── file1.js │ │ │ ├── file10.js │ │ │ ├── file11.js │ │ │ ├── file12.js │ │ │ ├── file13.js │ │ │ ├── file14.js │ │ │ ├── file2.js │ │ │ ├── file3.js │ │ │ ├── file4.js │ │ │ ├── file5.js │ │ │ ├── file6.js │ │ │ ├── file7.js │ │ │ ├── file8.js │ │ │ └── file9.js │ │ ├── noop-transport.js │ │ ├── pretty/ │ │ │ └── null-prototype.js │ │ ├── stdout-hack-protection.js │ │ ├── syncfalse-child.js │ │ ├── syncfalse-exit.js │ │ ├── syncfalse-flush-exit.js │ │ ├── syncfalse.js │ │ ├── syntax-error-esm.mjs │ │ ├── to-file-transport-with-transform.js │ │ ├── to-file-transport.js │ │ ├── to-file-transport.mjs │ │ ├── transport/ │ │ │ ├── index.js │ │ │ └── package.json │ │ ├── transport-exit-immediately-with-async-dest.js │ │ ├── transport-exit-immediately.js │ │ ├── transport-exit-on-ready.js │ │ ├── transport-invalid-node-options.js │ │ ├── transport-main.js │ │ ├── transport-many-lines.js │ │ ├── transport-preload-main.mjs │ │ ├── transport-preload.mjs │ │ ├── transport-string-stdout.js │ │ ├── transport-transform.js │ │ ├── transport-uses-pino-config.js │ │ ├── transport-with-on-exit.js │ │ ├── transport-worker-data.js │ │ ├── transport-worker-name.js │ │ ├── transport-worker.js │ │ ├── transport-wrong-export-type.js │ │ └── ts/ │ │ ├── to-file-transport-native.mts │ │ ├── to-file-transport-with-transform.ts │ │ ├── to-file-transport.ts │ │ ├── transpile.cjs │ │ ├── transport-exit-immediately-with-async-dest.ts │ │ ├── transport-exit-immediately.ts │ │ ├── transport-exit-on-ready.ts │ │ ├── transport-main.ts │ │ ├── transport-string-stdout.ts │ │ └── transport-worker.ts │ ├── formatters.test.js │ ├── helper.d.ts │ ├── helper.js │ ├── hooks.test.js │ ├── http.test.js │ ├── internals/ │ │ └── version.test.js │ ├── is-level-enabled.test.js │ ├── jest/ │ │ └── basic.spec.js │ ├── levels.test.js │ ├── metadata.test.js │ ├── mixin-merge-strategy.test.js │ ├── mixin.test.js │ ├── multistream.test.js │ ├── redact.test.js │ ├── serializers.test.js │ ├── stdout-protection.test.js │ ├── syncfalse.test.js │ ├── timestamp-nano.test.js │ ├── timestamp.test.js │ ├── transport/ │ │ ├── big.test.js │ │ ├── bundlers-support.test.js │ │ ├── caller.test.js │ │ ├── core.test.js │ │ ├── core.transpiled.test.ts │ │ ├── crash.test.js │ │ ├── module-link.test.js │ │ ├── native-type-stripping.test.mjs │ │ ├── node-options.test.js │ │ ├── pipeline.test.js │ │ ├── preload.test.js │ │ ├── repl.test.js │ │ ├── sync-false.test.js │ │ ├── sync-true.test.js │ │ ├── targets.test.js │ │ └── uses-pino-config.test.js │ ├── transport-stream.test.js │ └── types/ │ ├── pino-import.test-d.cts │ ├── pino-multistream.test-d.ts │ ├── pino-top-export.test-d.ts │ ├── pino-transport.test-d.ts │ ├── pino-type-only.test-d.ts │ ├── pino.test-d.ts │ ├── pino.ts │ └── tsconfig.json ├── tsconfig.json └── tstyche.config.json
SYMBOL INDEX (371 symbols across 74 files)
FILE: benchmarks/formatters.bench.js
method level (line 4) | level (label, number) {
method bindings (line 11) | bindings (bindings) {
method log (line 21) | log (obj) {
FILE: benchmarks/utils/runbench.js
function usage (line 11) | function usage () {
function runBenchmark (line 54) | function runBenchmark (name, done) {
function sum (line 92) | function sum (arr) {
function displayResults (line 100) | function displayResults (results) {
function toBench (line 122) | function toBench (done) {
FILE: benchmarks/utils/wrap-log-level.js
function build (line 11) | function build (dest) {
FILE: browser.js
constant CENSOR (line 8) | const CENSOR = '[Redacted]'
function handleRedactOpts (line 10) | function handleRedactOpts (opts) {
function buildRedactFn (line 21) | function buildRedactFn (redact) {
function levelToValue (line 44) | function levelToValue (level, logger) {
function appendChildLogger (line 61) | function appendChildLogger (parentLogger, childLogger) {
function setupBaseLogFunctions (line 69) | function setupBaseLogFunctions (logger, levels, proto) {
function shouldSerialize (line 77) | function shouldSerialize (serialize, serializers) {
function pino (line 90) | function pino (opts) {
function getLevels (line 245) | function getLevels (opts) {
function invertObject (line 257) | function invertObject (obj) {
function getBindingChain (line 287) | function getBindingChain (logger) {
function set (line 305) | function set (self, opts, rootLogger, level) {
function prependBindingsInArguments (line 337) | function prependBindingsInArguments (bindings, logFunc) {
function createWrap (line 343) | function createWrap (self, opts, rootLogger, level) {
function asObject (line 394) | function asObject (logger, level, args, ts, opts) {
function applySerializers (line 447) | function applySerializers (args, serialize, serializers, stdErrSerialize) {
function transmit (line 461) | function transmit (logger, opts, args, argsIsSerialized = false) {
function createLogEventShape (line 492) | function createLogEventShape (bindings) {
function asErrValue (line 501) | function asErrValue (err) {
function getTimeFunction (line 515) | function getTimeFunction (opts) {
function mock (line 525) | function mock () { return {} }
function passthrough (line 526) | function passthrough (a) { return a }
function noop (line 527) | function noop () {}
function nullTime (line 529) | function nullTime () { return false }
function epochTime (line 530) | function epochTime () { return Date.now() }
function unixTime (line 531) | function unixTime () { return Math.round(Date.now() / 1000.0) }
function isoTime (line 532) | function isoTime () { return new Date(Date.now()).toISOString() }
function pfGlobalThisOrFallback (line 536) | function pfGlobalThisOrFallback () {
function getCallerLocation (line 559) | function getCallerLocation () {
FILE: lib/caller.js
function noOpPrepareStackTrace (line 3) | function noOpPrepareStackTrace (_, stack) {
FILE: lib/constants.js
constant DEFAULT_LEVELS (line 6) | const DEFAULT_LEVELS = {
constant SORTING_ORDER (line 20) | const SORTING_ORDER = {
FILE: lib/levels.js
function genLsCache (line 47) | function genLsCache (instance) {
function isStandardLevel (line 59) | function isStandardLevel (level, useOnlyCustomLevels) {
function setLevel (line 77) | function setLevel (level) {
function getLevel (line 108) | function getLevel (level) {
function isLevelEnabled (line 114) | function isLevelEnabled (logLevel) {
function compareLevel (line 129) | function compareLevel (direction, current, expected) {
function genLevelComparison (line 144) | function genLevelComparison (levelComparison) {
function mappings (line 152) | function mappings (customLevels = null, useOnlyCustomLevels = false) {
function assertDefaultLevelFound (line 175) | function assertDefaultLevelFound (defaultLevel, customLevels, useOnlyCus...
function assertNoLevelCollisions (line 198) | function assertNoLevelCollisions (levels, customLevels) {
function assertLevelComparison (line 217) | function assertLevelComparison (levelComparison) {
FILE: lib/multistream.js
constant DEFAULT_INFO_LEVEL (line 6) | const DEFAULT_INFO_LEVEL = DEFAULT_LEVELS.info
function multistream (line 8) | function multistream (streamsArray, opts) {
function compareByLevel (line 187) | function compareByLevel (a, b) {
function initLoopVar (line 191) | function initLoopVar (length, dedupe) {
function adjustLoopVar (line 195) | function adjustLoopVar (i, dedupe) {
function checkLoopVar (line 199) | function checkLoopVar (i, length, dedupe) {
FILE: lib/proto.js
method level (line 64) | get level () { return this[getLevelSym]() }
method level (line 65) | set level (lvl) { this[setLevelSym](lvl) }
method levelVal (line 66) | get levelVal () { return this[levelValSym] }
method levelVal (line 67) | set levelVal (n) { throw Error('levelVal is read-only') }
method msgPrefix (line 68) | get msgPrefix () { return this[msgPrefixSym] }
method [Symbol.toStringTag] (line 69) | get [Symbol.toStringTag] () { return 'Pino' }
function child (line 85) | function child (bindings, options) {
function bindings (line 180) | function bindings () {
function setBindings (line 189) | function setBindings (newBindings) {
function defaultMixinMergeStrategy (line 202) | function defaultMixinMergeStrategy (mergeObject, mixinObject) {
function write (line 206) | function write (_obj, msg, num) {
function flush (line 246) | function flush (cb) {
FILE: lib/redaction.js
constant CENSOR (line 9) | const CENSOR = '[Redacted]'
function redaction (line 12) | function redaction (opts, serialize) {
function handle (line 102) | function handle (opts) {
FILE: lib/time.js
constant NS_PER_MS (line 11) | const NS_PER_MS = 1_000_000n
constant NS_PER_SEC (line 12) | const NS_PER_SEC = 1_000_000_000n
FILE: lib/tools.js
function noop (line 37) | function noop () {
function genLog (line 40) | function genLog (level, hook) {
function _asString (line 88) | function _asString (str) {
function asJson (line 123) | function asJson (obj, msg, num, time) {
function _asJson (line 144) | function _asJson (obj, msg, num, time) {
function asChindings (line 238) | function asChindings (instance, bindings) {
function hasBeenTampered (line 267) | function hasBeenTampered (stream) {
function buildSafeSonicBoom (line 271) | function buildSafeSonicBoom (opts) {
function autoEnd (line 302) | function autoEnd (stream, eventName) {
function createArgsNormalizer (line 323) | function createArgsNormalizer (defaultOptions) {
function stringify (line 375) | function stringify (obj, stringifySafeFn) {
function buildFormatters (line 388) | function buildFormatters (level, bindings, log) {
function normalizeDestFileDescriptor (line 404) | function normalizeDestFileDescriptor (destination) {
FILE: lib/transport-stream.js
function loadTransportStreamBuilder (line 13) | async function loadTransportStreamBuilder (target) {
FILE: lib/transport.js
function setupOnExit (line 14) | function setupOnExit (stream) {
function hasPreloadFlags (line 28) | function hasPreloadFlags () {
function sanitizeNodeOptions (line 42) | function sanitizeNodeOptions (nodeOptions) {
function shouldDropPreload (line 84) | function shouldDropPreload (value) {
function stripQuotes (line 102) | function stripQuotes (value) {
function buildStream (line 113) | function buildStream (filename, workerData, workerOpts, sync, name) {
function autoEnd (line 179) | function autoEnd (stream) {
function flush (line 188) | function flush (stream) {
function transport (line 192) | function transport (fullOptions) {
FILE: lib/worker.js
method close (line 132) | close (err, cb) {
function process (line 150) | function process (stream) {
function createPipeline (line 173) | function createPipeline (streams) {
FILE: pino.d.ts
type TimeFn (line 26) | type TimeFn = () => string
type MixinFn (line 27) | type MixinFn<CustomLevels extends string = never> = (mergeObject: object...
type MixinMergeStrategyFn (line 28) | type MixinMergeStrategyFn = (mergeObject: object, mixinObject: object) =...
type CustomLevelLogger (line 30) | type CustomLevelLogger<CustomLevels extends string, UseOnlyCustomLevels ...
type OnChildCallback (line 50) | type OnChildCallback<CustomLevels extends string = never> = (child: Logg...
type redactOptions (line 52) | interface redactOptions {
type LoggerExtras (line 58) | interface LoggerExtras<CustomLevels extends string = never, UseOnlyCusto...
type BaseLogger (line 133) | interface BaseLogger {
type Bindings (line 224) | type Bindings = Record<string, any>
type Level (line 226) | type Level = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace'
type LevelOrString (line 227) | type LevelOrString = Level | (string & {})
type LevelWithSilent (line 228) | type LevelWithSilent = Level | 'silent'
type LevelWithSilentOrString (line 229) | type LevelWithSilentOrString = LevelWithSilent | (string & {})
type SerializerFn (line 231) | type SerializerFn = (value: any) => any
type WriteFn (line 232) | type WriteFn = (o: object) => void
type LevelChangeEventListener (line 234) | type LevelChangeEventListener<CustomLevels extends string = never, UseOn...
type LogDescriptor (line 242) | type LogDescriptor = Record<string, any>
type Logger (line 244) | type Logger<CustomLevels extends string = never, UseOnlyCustomLevels ext...
type SerializedError (line 246) | type SerializedError = pinoStdSerializers.SerializedError
type SerializedResponse (line 247) | type SerializedResponse = pinoStdSerializers.SerializedResponse
type SerializedRequest (line 248) | type SerializedRequest = pinoStdSerializers.SerializedRequest
type TransportTargetOptions (line 250) | interface TransportTargetOptions<TransportOptions = Record<string, any>> {
type TransportBaseOptions (line 256) | interface TransportBaseOptions<TransportOptions = Record<string, any>> {
type TransportSingleOptions (line 261) | interface TransportSingleOptions<TransportOptions = Record<string, any>>...
type TransportPipelineOptions (line 265) | interface TransportPipelineOptions<TransportOptions = Record<string, any...
type TransportMultiOptions (line 270) | interface TransportMultiOptions<TransportOptions = Record<string, any>> ...
type MultiStreamOptions (line 276) | interface MultiStreamOptions {
type DestinationStream (line 281) | interface DestinationStream {
type DestinationStreamHasMetadata (line 285) | interface DestinationStreamHasMetadata {
type DestinationStreamWithMetadata (line 294) | type DestinationStreamWithMetadata = DestinationStream & ({ [symbols.nee...
type StreamEntry (line 296) | interface StreamEntry<TLevel = Level> {
type MultiStreamRes (line 301) | interface MultiStreamRes<TOriginLevel = Level> {
type LevelMapping (line 310) | interface LevelMapping {
type PlaceholderSpecifier (line 321) | type PlaceholderSpecifier = 'd' | 's' | 'j' | 'o' | 'O'
type PlaceholderTypeMapping (line 322) | type PlaceholderTypeMapping<T extends PlaceholderSpecifier> = T extends 'd'
type ParseLogFnArgs (line 330) | type ParseLogFnArgs<
type LogFnFields (line 339) | interface LogFnFields {}
type LogFn (line 341) | interface LogFn {
type LoggerOptions (line 350) | interface LoggerOptions<CustomLevels extends string = never, UseOnlyCust...
type ChildLoggerOptions (line 710) | interface ChildLoggerOptions<CustomLevels extends string = never> {
type LogEvent (line 731) | interface LogEvent {
FILE: pino.js
method bindings (line 66) | bindings (bindings) {
method level (line 69) | level (label, number) {
function pino (line 90) | function pino (...args) {
FILE: test/basic.test.js
function levelTest (line 71) | function levelTest (name, level) {
method test (line 226) | test () { return undefined }
method log (line 397) | log (input) {
method toJSON (line 512) | toJSON () { return undefined }
method write (line 521) | write (result) {
FILE: test/broken-pipe.test.js
function testFile (line 26) | function testFile (file) {
FILE: test/browser-child.test.js
method write (line 60) | write (actual) {
method write (line 92) | write (actual) {
function checkLogObjects (line 123) | function checkLogObjects (is, same, actual, expected) {
FILE: test/browser-disabled.test.js
method write (line 9) | write (actual) {
method write (line 39) | write (actual) {
method write (line 68) | write (actual) {
function checkLogObjects (line 80) | function checkLogObjects (same, actual, expected, is) {
FILE: test/browser-levels.test.js
method write (line 18) | write (actual) {
method write (line 46) | write (actual) {
method write (line 74) | write (actual) {
method send (line 79) | send () {}
method write (line 105) | write (actual) {
method write (line 130) | write (actual) {
method write (line 177) | write () {
method write (line 193) | write () {
function checkLogObjects (line 232) | function checkLogObjects (is, same, actual, expected) {
FILE: test/browser-redaction.test.js
method write (line 12) | write (o) {
method write (line 28) | write (o) {
method write (line 47) | write (o) {
method write (line 63) | write (o) {
method write (line 86) | write (o) {
method write (line 115) | write (o) {
method write (line 132) | write (o) {
method write (line 155) | write (o) {
method write (line 171) | write (o, ...args) {
method write (line 188) | write (o, ...args) {
method write (line 206) | write (o, ...args) {
method write (line 226) | write (o) {
method level (line 234) | level (label, number) {
method write (line 248) | write (o) {
method level (line 256) | level (label, number) {
FILE: test/browser-serializers.test.js
method write (line 22) | write (o) {
method write (line 36) | write (o) {
method write (line 253) | write (o) {
method write (line 274) | write (o) {
method write (line 289) | write (o) {
method write (line 335) | write (o) {
FILE: test/browser-transmit.test.js
function noop (line 5) | function noop () {}
method send (line 27) | send () { is(c, 1) }
method write (line 39) | write () {}
method send (line 41) | send (level) {
method send (line 57) | send (level, { messages }) {
method send (line 75) | send (level, { messages }) {
method send (line 94) | send (level, { messages }) {
method send (line 113) | send (level, { messages }) {
method write (line 131) | write (o) {
method send (line 135) | send (level, logEvent) {
method send (line 151) | send (level, logEvent) {
method send (line 175) | send (level, logEvent) {
method send (line 197) | send (level) {
method send (line 218) | send (level) {
method send (line 240) | send (level) {
method send (line 265) | send (level, logEvent) {
method send (line 295) | send (level, logEvent) {
method send (line 322) | send (level, logEvent) {
method write (line 356) | write (o) {
method send (line 360) | send () { }
method write (line 375) | write () {
method send (line 379) | send () {
method write (line 398) | write () {
method send (line 403) | send () {
FILE: test/browser.test.js
method level (line 218) | level (label, number) {
method log (line 242) | log (o) {
method extras (line 275) | extras (data) {
method send (line 283) | send (level, o) {
method extras (line 297) | extras (data) {
method extras (line 317) | extras (data) {
method send (line 325) | send (level, o) {
method write (line 478) | write (o) {
method write (line 495) | write (o) {
method write (line 513) | write (o) {
method error (line 552) | error (o) {
function levelTest (line 566) | function levelTest (name) {
function consoleMethodTest (line 659) | function consoleMethodTest (level, method) {
function absentConsoleMethodTest (line 671) | function absentConsoleMethodTest (method, fallback) {
function isFunc (line 685) | function isFunc (fn) { return typeof fn === 'function' }
function fnName (line 686) | function fnName (fn) {
function sink (line 691) | function sink (method, fn) {
FILE: test/complex-objects.test.js
method get (line 14) | get () { throw new Error('kaboom') }
method get (line 28) | get () { throw new Error('kaboom') }
FILE: test/crlf.test.js
function capture (line 9) | function capture () {
FILE: test/custom-levels.test.js
method level (line 236) | level (label, number) {
method level (line 256) | level (label, number) {
FILE: test/diagnostics.test.js
constant AS_JSON_START (line 13) | const AS_JSON_START = 'tracing:pino_asJson:start'
constant AS_JSON_END (line 14) | const AS_JSON_END = 'tracing:pino_asJson:end'
method write (line 29) | write (data, enc, cb) {
function startHandler (line 60) | function startHandler (event) {
function endHandler (line 67) | function endHandler (event) {
function startHandler (line 98) | function startHandler () {
function endHandler (line 103) | function endHandler () {
FILE: test/error.test.js
class Bar (line 42) | class Bar extends Error {}
method mixin (line 185) | mixin () {
method err (line 233) | err () {}
method mixin (line 251) | mixin () {
method err (line 299) | err () {}
FILE: test/escaping.test.js
function testEscape (line 13) | function testEscape (ch, key) {
FILE: test/fixtures/console-transport.js
method write (line 6) | write (chunk, enc, cb) {
FILE: test/fixtures/crashing-transport.js
method write (line 6) | write (chunk, enc, cb) {
FILE: test/fixtures/noop-transport.js
method write (line 6) | write (chunk, enc, cb) {
FILE: test/fixtures/to-file-transport-with-transform.js
function run (line 7) | async function run (opts) {
FILE: test/fixtures/to-file-transport.js
function run (line 6) | async function run (opts) {
FILE: test/fixtures/to-file-transport.mjs
function run (line 4) | async function run (opts) {
FILE: test/fixtures/transport-many-lines.js
function run (line 20) | function run () {
FILE: test/fixtures/transport-transform.js
method transform (line 10) | transform (chunk, enc, cb) {
FILE: test/fixtures/transport-uses-pino-config.js
method transform (line 10) | transform (chunk, enc, cb) {
FILE: test/fixtures/transport-worker-data.js
method write (line 9) | write (chunk, enc, cb) {
FILE: test/fixtures/transport-worker-name.js
method write (line 11) | write (chunk, enc, cb) {
FILE: test/fixtures/transport-worker.js
method write (line 8) | write (chunk, enc, cb) {
FILE: test/fixtures/transport/index.js
function run (line 6) | async function run (opts) {
FILE: test/fixtures/ts/to-file-transport-with-transform.ts
function run (line 5) | async function run (opts: { destination?: fs.PathLike }): Promise<Transf...
FILE: test/fixtures/ts/to-file-transport.ts
function run (line 4) | async function run (opts: { destination?: fs.PathLike }): Promise<fs.Wri...
FILE: test/fixtures/ts/transpile.cjs
function transpile (line 13) | async function transpile () {
FILE: test/fixtures/ts/transport-worker.ts
method write (line 6) | write (chunk, _enc, cb) {
FILE: test/formatters.test.js
method level (line 18) | level (label, number) {
method bindings (line 41) | bindings (bindings) {
method bindings (line 70) | bindings (bindings) {
method log (line 89) | log (obj) {
method level (line 111) | level (label, number) {
method bindings (line 118) | bindings (bindings) {
method log (line 128) | log (obj) {
method level (line 156) | level (label, number) {
method bindings (line 163) | bindings (bindings) {
method log (line 173) | log (obj) {
method bindings (line 184) | bindings (bindings) {
method level (line 213) | level (label, number) {
method bindings (line 220) | bindings (bindings) {
method log (line 230) | log (obj) {
method log (line 241) | log (obj) {
method level (line 269) | level (label, number) {
method bindings (line 277) | bindings (bindings) {
method log (line 287) | log (obj) {
method log (line 316) | log (obj) {
method level (line 347) | level (label) {
FILE: test/helper.js
function getPathToNull (line 17) | function getPathToNull () {
function once (line 21) | function once (emitter, name) {
function sink (line 31) | function sink (func) {
function check (line 44) | function check (is, chunk, level, msg) {
function sleep (line 53) | function sleep (ms) {
function watchFileCreated (line 59) | function watchFileCreated (filename) {
function watchForWrite (line 84) | function watchForWrite (filename, testString) {
function file (line 106) | function file () {
function match (line 131) | function match (found, expected) {
FILE: test/hooks.test.js
method logMethod (line 16) | logMethod (args, method, level) {
method logMethod (line 42) | logMethod (args, method) {
method logMethod (line 60) | logMethod (args, method) {
method logMethod (line 84) | logMethod (args, method, level) {
method streamWrite (line 104) | streamWrite (s) {
FILE: test/levels.test.js
function handle (line 103) | function handle (lvl, val, prevLvl, prevVal, logger) {
method level (line 261) | level (label, number) {
method level (line 302) | level (label, number) {
method level (line 332) | level (label, number) {
method level (line 365) | level (label, number) {
method level (line 390) | level (label, number) {
FILE: test/metadata.test.js
method write (line 17) | write (chunk) {
method write (line 45) | write (chunk) {
method write (line 74) | write (chunk) {
method write (line 100) | write (chunk) {
FILE: test/mixin-merge-strategy.test.js
method mixin (line 16) | mixin () {
method mixin (line 38) | mixin () {
method mixinMergeStrategy (line 41) | mixinMergeStrategy (mergeObject, mixinObject) {
FILE: test/mixin.test.js
method mixin (line 20) | mixin () {
method mixin (line 44) | mixin () {
method mixin (line 73) | mixin () {
method mixin (line 84) | mixin () {
method mixin (line 114) | mixin (context) {
method mixin (line 140) | mixin (context) {
method mixin (line 159) | mixin (context, num) {
method mixin (line 182) | mixin (context, num, logger) {
method mixin (line 202) | mixin (context, num, logger) {
method mixin (line 225) | mixin (context, num, logger) {
FILE: test/multistream.test.js
method write (line 310) | write (chunk) {
method write (line 335) | write (chunk) {
method write (line 359) | write (chunk) {
FILE: test/redact.test.js
method censor (line 858) | censor (value, path) {
FILE: test/serializers.test.js
function a (line 139) | function a () {
function a (line 178) | function a () {
function b (line 182) | function b () {
FILE: test/syncfalse.test.js
function getOutputLogLines (line 152) | async function getOutputLogLines () {
FILE: test/transport/big.test.js
method write (line 36) | write (chunk, enc, cb) {
FILE: test/transport/core.test.js
method level (line 519) | level (label) {
FILE: test/transport/core.transpiled.test.ts
function runTests (line 17) | function runTests (esVersion: string): void {
FILE: test/transport/module-link.test.js
function installTransportModule (line 18) | async function installTransportModule (target) {
function uninstallTransportModule (line 36) | async function uninstallTransportModule () {
FILE: test/transport/native-type-stripping.test.mjs
function supportsTypeStripping (line 16) | function supportsTypeStripping () {
FILE: test/transport/node-options.test.js
function buildTransportWithFakeThreadStream (line 10) | function buildTransportWithFakeThreadStream () {
FILE: test/transport/sync-false.test.js
function getOutputLogLines (line 40) | async function getOutputLogLines () {
FILE: test/transport/sync-true.test.js
function getOutputLogLines (line 14) | function getOutputLogLines () {
FILE: test/transport/targets.test.js
method destination (line 15) | destination (opts) {
FILE: test/transport/uses-pino-config.test.js
function serializeError (line 16) | function serializeError (error) {
function parseLogs (line 24) | function parseLogs (buffer) {
FILE: test/types/pino-transport.test-d.ts
type TransportConfig (line 143) | type TransportConfig = {
FILE: test/types/pino-type-only.test-d.ts
function createStream (line 45) | function createStream (): DestinationStreamWithMetadata {
method log (line 60) | log (obj) {
method level (line 63) | level (label, number) {
FILE: test/types/pino.test-d.ts
method write (line 139) | write (o) {}
method mixin (line 143) | mixin () {
method write (line 200) | write (o) {}
method info (line 207) | info (o) {}
method error (line 208) | error (o) {}
method test (line 250) | test () {
method logMethod (line 339) | logMethod (args, method, level) {
method streamWrite (line 343) | streamWrite (s) {
type StrictShape (line 413) | interface StrictShape {
type CustomLogger (line 429) | interface CustomLogger extends pino.Logger {
type CustomBaseLogger (line 436) | interface CustomBaseLogger extends pino.BaseLogger {
method fatal (line 442) | fatal () {}
method error (line 443) | error () {}
method warn (line 444) | warn () {}
method info (line 445) | info () {}
method debug (line 446) | debug () {}
method trace (line 447) | trace () {}
method silent (line 448) | silent () {}
method child (line 449) | child () {
type CustomLevelLogger (line 495) | type CustomLevelLogger = typeof customLevelLogger
type CustomLevelLoggerLevels (line 496) | type CustomLevelLoggerLevels = pino.Level | keyof typeof customLevels
function dangerous (line 517) | function dangerous () {
type LogParam (line 547) | type LogParam = Parameters<LogFn>
method logMethod (line 564) | logMethod (this, parameters, method) {
type LogFnFields (line 668) | interface LogFnFields {
FILE: test/types/pino.ts
type CustomLevels (line 69) | type CustomLevels = keyof typeof customLevels
Condensed preview — 207 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (705K chars).
[
{
"path": ".github/dependabot.yml",
"chars": 274,
"preview": "version: 2\nupdates:\n - package-ecosystem: \"github-actions\"\n directory: \"/\"\n schedule:\n interval: \"monthly\"\n "
},
{
"path": ".github/workflows/bench.yml",
"chars": 1437,
"preview": "name: Benchmarks\non:\n push:\n branches:\n - main\n paths-ignore:\n - 'docs/**'\n - '*.md'\n pull_re"
},
{
"path": ".github/workflows/ci.yml",
"chars": 2209,
"preview": "name: CI\n\non:\n push:\n branches:\n - main\n - 'v*'\n paths-ignore:\n - 'docs/**'\n - '*.md'\n pull_"
},
{
"path": ".github/workflows/lock-threads.yml",
"chars": 801,
"preview": "name: 'Lock Threads'\n\non:\n schedule:\n - cron: '0 0 * * *'\n workflow_dispatch:\n\npermissions:\n issues: write\n pull-"
},
{
"path": ".github/workflows/publish-release.yml",
"chars": 1493,
"preview": "name: Publish release\n\non:\n workflow_dispatch:\n inputs:\n version:\n description: 'The version number to t"
},
{
"path": ".github/workflows/target-main.yml",
"chars": 549,
"preview": "name: PR Target Check\n\non:\n pull_request_target:\n types: [opened]\n\npermissions:\n pull-requests: write\n\njobs:\n comm"
},
{
"path": ".gitignore",
"chars": 2391,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n.pnpm-debug.log*\n\n# Diagnostic reports"
},
{
"path": ".nojekyll",
"chars": 0,
"preview": ""
},
{
"path": ".npmignore",
"chars": 47,
"preview": "*.png\n.github/\ndocsify/\n.nyc_output/\ncoverage/\n"
},
{
"path": ".npmrc",
"chars": 19,
"preview": "package-lock=false\n"
},
{
"path": ".prettierignore",
"chars": 2,
"preview": "*\n"
},
{
"path": "CNAME",
"chars": 10,
"preview": "getpino.io"
},
{
"path": "CONTRIBUTING.md",
"chars": 1505,
"preview": "# Pino is an OPEN Open Source Project\n\n## What?\n\nIndividuals making significant and valuable contributions are given com"
},
{
"path": "LICENSE",
"chars": 1209,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2016-2025 Matteo Collina, David Mark Clements and the Pino contributors listed at <"
},
{
"path": "README.md",
"chars": 4864,
"preview": "\n\n# pino\n[](https://www.npmjs.com/package/pin"
},
{
"path": "SECURITY.md",
"chars": 3412,
"preview": "# Security Policy\n\nThis document describes the management of vulnerabilities for the\nPino project and all modules within"
},
{
"path": "benchmarks/basic.bench.js",
"chars": 2200,
"preview": "'use strict'\n\nconst bench = require('fastbench')\nconst pino = require('../')\nconst bunyan = require('bunyan')\nconst bole"
},
{
"path": "benchmarks/child-child.bench.js",
"chars": 1463,
"preview": "'use strict'\n\nconst bench = require('fastbench')\nconst pino = require('../')\nconst bunyan = require('bunyan')\nconst fs ="
},
{
"path": "benchmarks/child-creation.bench.js",
"chars": 1905,
"preview": "'use strict'\n\nconst bench = require('fastbench')\nconst pino = require('../')\nconst bunyan = require('bunyan')\nconst bole"
},
{
"path": "benchmarks/child.bench.js",
"chars": 1579,
"preview": "'use strict'\n\nconst bench = require('fastbench')\nconst pino = require('../')\nconst bunyan = require('bunyan')\nconst bole"
},
{
"path": "benchmarks/deep-object.bench.js",
"chars": 2065,
"preview": "'use strict'\n\nconst bench = require('fastbench')\nconst pino = require('../')\nconst bunyan = require('bunyan')\nconst bole"
},
{
"path": "benchmarks/formatters.bench.js",
"chars": 1009,
"preview": "'use strict'\n\nconst formatters = {\n level (label, number) {\n return {\n log: {\n level: label\n }\n "
},
{
"path": "benchmarks/internal/custom-levels.js",
"chars": 1555,
"preview": "'use strict'\n\nconst bench = require('fastbench')\nconst pino = require('../../')\n\nconst base = pino(pino.destination('/de"
},
{
"path": "benchmarks/internal/just-pino-heavy.bench.js",
"chars": 1920,
"preview": "'use strict'\n\nconst bench = require('fastbench')\nconst pino = require('../../')\nconst fs = require('node:fs')\nconst dest"
},
{
"path": "benchmarks/internal/just-pino.bench.js",
"chars": 5037,
"preview": "'use strict'\n\nconst bench = require('fastbench')\nconst pino = require('../../')\nconst fs = require('node:fs')\nconst dest"
},
{
"path": "benchmarks/internal/parent-vs-child.bench.js",
"chars": 1966,
"preview": "'use strict'\n\nconst bench = require('fastbench')\nconst pino = require('../../')\n\nconst base = pino(pino.destination('/de"
},
{
"path": "benchmarks/internal/redact.bench.js",
"chars": 2748,
"preview": "'use strict'\n\nconst bench = require('fastbench')\nconst pino = require('../../')\nconst fs = require('node:fs')\nconst dest"
},
{
"path": "benchmarks/long-string.bench.js",
"chars": 1813,
"preview": "'use strict'\n\nconst bench = require('fastbench')\nconst pino = require('../')\nconst bunyan = require('bunyan')\nconst bole"
},
{
"path": "benchmarks/multi-arg.bench.js",
"chars": 5386,
"preview": "'use strict'\n\nconst bench = require('fastbench')\nconst pino = require('../')\nconst bunyan = require('bunyan')\nconst bole"
},
{
"path": "benchmarks/multistream.js",
"chars": 2397,
"preview": "'use strict'\n\nconst bench = require('fastbench')\nconst bunyan = require('bunyan')\nconst pino = require('../')\nconst fs ="
},
{
"path": "benchmarks/object.bench.js",
"chars": 2012,
"preview": "'use strict'\n\nconst bench = require('fastbench')\nconst pino = require('../')\nconst bunyan = require('bunyan')\nconst bole"
},
{
"path": "benchmarks/utils/generate-benchmark-doc.js",
"chars": 722,
"preview": "'use strict'\nconst { join } = require('node:path')\nconst { execSync } = require('node:child_process')\n\nconst run = (type"
},
{
"path": "benchmarks/utils/runbench.js",
"chars": 3743,
"preview": "'use strict'\n\nconst { type, platform, arch, release, cpus } = require('node:os')\nconst { resolve, join } = require('node"
},
{
"path": "benchmarks/utils/wrap-log-level.js",
"chars": 1480,
"preview": "'use strict'\n\nconst { readFileSync } = require('node:fs')\nconst vm = require('vm')\nconst { join } = require('node:path')"
},
{
"path": "bin.js",
"chars": 170,
"preview": "#!/usr/bin/env node\nconsole.error(\n '`pino` cli has been removed. Use `pino-pretty` cli instead.\\n' +\n '\\nSee: https:/"
},
{
"path": "browser.js",
"chars": 16691,
"preview": "'use strict'\n\nconst format = require('quick-format-unescaped')\nconst Redact = require('@pinojs/redact')\n\nmodule.exports "
},
{
"path": "build/sync-version.js",
"chars": 751,
"preview": "'use strict'\n\nconst fs = require('node:fs')\nconst path = require('node:path')\nlet { version } = require('../package.json"
},
{
"path": "docs/api.md",
"chars": 52751,
"preview": "# API\n\n* [pino() => logger](#export)\n * [options](#options)\n * [destination](#destination)\n * [destination\\[Symbol.fo"
},
{
"path": "docs/asynchronous.md",
"chars": 2256,
"preview": "# Asynchronous Logging\n\nAsynchronous logging enables the minimum overhead of Pino.\nAsynchronous logging works by bufferi"
},
{
"path": "docs/benchmarks.md",
"chars": 1136,
"preview": "\n# Benchmarks\n\n`pino.info('hello world')`:\n\n```\n\nBASIC benchmark averages\nBunyan average: 377.434ms\nWinston average: 270"
},
{
"path": "docs/browser.md",
"chars": 9032,
"preview": "# Browser API\n\nPino is compatible with [`browserify`](https://npm.im/browserify) for browser-side usage:\n\nThis can be us"
},
{
"path": "docs/bundling.md",
"chars": 1810,
"preview": "# Bundling\n\nDue to its internal architecture based on Worker Threads, it is not possible to bundle Pino *without* genera"
},
{
"path": "docs/child-loggers.md",
"chars": 2826,
"preview": "# Child loggers\n\nLet's assume we want to have `\"module\":\"foo\"` added to every log within a\nmodule `foo.js`.\n\nTo accompli"
},
{
"path": "docs/diagnostics.md",
"chars": 839,
"preview": "# Diagnostics\n\nPino provides [tracing channel](tc) events that allow insight into the\ninternal workings of the library. "
},
{
"path": "docs/ecosystem.md",
"chars": 5839,
"preview": "# Pino Ecosystem\n\nThis is a list of ecosystem modules that integrate with `pino`.\n\nModules listed under [Core](#core) ar"
},
{
"path": "docs/help.md",
"chars": 11868,
"preview": "# Help\n\n* [Log rotation](#rotate)\n* [Reopening log files](#reopening)\n* [Saving to multiple files](#multiple)\n* [Log fil"
},
{
"path": "docs/lts.md",
"chars": 2929,
"preview": "## Long Term Support\n\nPino's Long Term Support (LTS) is provided according to the schedule laid\nout in this document:\n\n1"
},
{
"path": "docs/pretty.md",
"chars": 1102,
"preview": "# Pretty Printing\n\nBy default, Pino log lines are newline delimited JSON (NDJSON). This is perfect\nfor production usage "
},
{
"path": "docs/redaction.md",
"chars": 4187,
"preview": "# Redaction\n\nTo redact sensitive information, supply paths to keys that hold sensitive data\nusing the `redact` option. N"
},
{
"path": "docs/transports.md",
"chars": 44146,
"preview": "# Transports\n\nPino transports can be used for both transmitting and transforming log output.\n\nThe way Pino generates log"
},
{
"path": "docs/web.md",
"chars": 6147,
"preview": "# Web Frameworks\n\nSince HTTP logging is a primary use case, Pino has first-class support for the Node.js\nweb framework e"
},
{
"path": "docsify/sidebar.md",
"chars": 1214,
"preview": "* [Readme](/)\n* [API](/docs/api.md)\n* [Browser API](/docs/browser.md)\n* [Redaction](/docs/redaction.md)\n* [Child Loggers"
},
{
"path": "eslint.config.js",
"chars": 741,
"preview": "'use strict'\n\nconst { defineConfig, globalIgnores } = require('eslint/config')\nconst neostandard = require('neostandard'"
},
{
"path": "examples/basic.js",
"chars": 1193,
"preview": "'use strict'\n\n// Pino's primary usage writes ndjson to `stdout`:\nconst pino = require('..')()\n\n// However, if \"human rea"
},
{
"path": "examples/transport.js",
"chars": 1500,
"preview": "'use strict'\n\nconst pino = require('..')\nconst { tmpdir } = require('node:os')\nconst { join } = require('node:path')\n\nco"
},
{
"path": "file.js",
"chars": 358,
"preview": "'use strict'\n\nconst pino = require('./pino')\nconst { once } = require('node:events')\n\nmodule.exports = async function (o"
},
{
"path": "inc-version.sh",
"chars": 821,
"preview": "#!/bin/bash \n\nset -e\n\nPATH=./node_modules/.bin:${PATH}\nCURRENT_VERSION=$(jq -r .version package.json)\n\ncase ${1} in\n Ma"
},
{
"path": "index.html",
"chars": 1826,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <title>Pino - Super fast, all natural JSON logger for"
},
{
"path": "lib/caller.js",
"chars": 551,
"preview": "'use strict'\n\nfunction noOpPrepareStackTrace (_, stack) {\n return stack\n}\n\nmodule.exports = function getCallers () {\n "
},
{
"path": "lib/constants.js",
"chars": 375,
"preview": "/**\n * Represents default log level values\n *\n * @enum {number}\n */\nconst DEFAULT_LEVELS = {\n trace: 10,\n debug: 20,\n "
},
{
"path": "lib/deprecations.js",
"chars": 183,
"preview": "'use strict'\n\nconst warning = require('process-warning')()\nmodule.exports = warning\n\n// const warnName = 'PinoWarning'\n\n"
},
{
"path": "lib/levels.js",
"chars": 6707,
"preview": "'use strict'\n/* eslint no-prototype-builtins: 0 */\nconst {\n lsCacheSym,\n levelValSym,\n useOnlyCustomLevelsSym,\n stre"
},
{
"path": "lib/meta.js",
"chars": 53,
"preview": "'use strict'\n\nmodule.exports = { version: '10.3.1' }\n"
},
{
"path": "lib/multistream.js",
"chars": 4746,
"preview": "'use strict'\n\nconst metadata = Symbol.for('pino.metadata')\nconst { DEFAULT_LEVELS } = require('./constants')\n\nconst DEFA"
},
{
"path": "lib/proto.js",
"chars": 7340,
"preview": "'use strict'\n\n/* eslint no-prototype-builtins: 0 */\n\nconst { EventEmitter } = require('node:events')\nconst {\n lsCacheSy"
},
{
"path": "lib/redaction.js",
"chars": 3170,
"preview": "'use strict'\n\nconst Redact = require('@pinojs/redact')\nconst { redactFmtSym, wildcardFirstSym } = require('./symbols')\n\n"
},
{
"path": "lib/symbols.js",
"chars": 2207,
"preview": "'use strict'\n\nconst setLevelSym = Symbol('pino.setLevel')\nconst getLevelSym = Symbol('pino.getLevel')\nconst levelValSym "
},
{
"path": "lib/time.js",
"chars": 1387,
"preview": "'use strict'\n\nconst nullTime = () => ''\n\nconst epochTime = () => `,\"time\":${Date.now()}`\n\nconst unixTime = () => `,\"time"
},
{
"path": "lib/tools.js",
"chars": 12936,
"preview": "'use strict'\n\n/* eslint no-prototype-builtins: 0 */\n\nconst diagChan = require('node:diagnostics_channel')\nconst format ="
},
{
"path": "lib/transport-stream.js",
"chars": 2246,
"preview": "'use strict'\n\nconst { realImport, realRequire } = require('real-require')\n\nmodule.exports = loadTransportStreamBuilder\n\n"
},
{
"path": "lib/transport.js",
"chars": 7519,
"preview": "'use strict'\n\nconst { createRequire } = require('module')\nconst { existsSync } = require('node:fs')\nconst getCallers = r"
},
{
"path": "lib/worker.js",
"chars": 7193,
"preview": "'use strict'\n\nconst EE = require('node:events')\nconst { pipeline, PassThrough } = require('node:stream')\nconst pino = re"
},
{
"path": "package.json",
"chars": 4203,
"preview": "{\n \"name\": \"pino\",\n \"version\": \"10.3.1\",\n \"description\": \"super fast, all natural json logger\",\n \"main\": \"pino.js\",\n"
},
{
"path": "pino.d.ts",
"chars": 39670,
"preview": "// Project: https://github.com/pinojs/pino.git, http://getpino.io\n// Definitions by: Peter Snider <https://github.com/ps"
},
{
"path": "pino.js",
"chars": 7142,
"preview": "'use strict'\n\nconst os = require('node:os')\nconst stdSerializers = require('pino-std-serializers')\nconst caller = requir"
},
{
"path": "test/basic.test.js",
"chars": 23484,
"preview": "'use strict'\n\nconst os = require('node:os')\nconst { readFileSync } = require('node:fs')\nconst test = require('node:test'"
},
{
"path": "test/broken-pipe.test.js",
"chars": 1705,
"preview": "'use strict'\n\nconst test = require('node:test')\nconst assert = require('node:assert')\nconst { join } = require('node:pat"
},
{
"path": "test/browser-child.test.js",
"chars": 2724,
"preview": "'use strict'\nconst test = require('tape')\nconst pino = require('../browser')\n\ntest('child has parent level', ({ end, sam"
},
{
"path": "test/browser-disabled.test.js",
"chars": 1697,
"preview": "'use strict'\nconst test = require('tape')\nconst pino = require('../browser')\n\ntest('set browser opts disabled to true', "
},
{
"path": "test/browser-early-console-freeze.test.js",
"chars": 212,
"preview": "'use strict'\nObject.freeze(console)\nconst test = require('tape')\nconst pino = require('../browser')\n\ntest('silent level'"
},
{
"path": "test/browser-is-level-enabled.test.js",
"chars": 3190,
"preview": "'use strict'\n\nconst { describe, test } = require('node:test')\nconst assert = require('node:assert')\nconst pino = require"
},
{
"path": "test/browser-levels.test.js",
"chars": 4451,
"preview": "'use strict'\nconst test = require('tape')\nconst pino = require('../browser')\n\ntest('set the level by string', ({ end, sa"
},
{
"path": "test/browser-redaction.test.js",
"chars": 5912,
"preview": "'use strict'\n// eslint-disable-next-line\nif (typeof $1 !== 'undefined') $1 = arguments.callee.caller.arguments[0]\n\nconst"
},
{
"path": "test/browser-serializers.test.js",
"chars": 8475,
"preview": "'use strict'\n// eslint-disable-next-line\nif (typeof $1 !== 'undefined') $1 = arguments.callee.caller.arguments[0]\n\nconst"
},
{
"path": "test/browser-timestamp.test.js",
"chars": 1759,
"preview": "'use strict'\nconst test = require('tape')\nconst pino = require('../browser')\n\nDate.now = () => 1599400603614\n\ntest('null"
},
{
"path": "test/browser-transmit.test.js",
"chars": 9243,
"preview": "'use strict'\nconst test = require('tape')\nconst pino = require('../browser')\n\nfunction noop () {}\n\ntest('throws if trans"
},
{
"path": "test/browser.test.js",
"chars": 16692,
"preview": "'use strict'\nconst test = require('tape')\nconst fresh = require('import-fresh')\nconst pinoStdSerializers = require('pino"
},
{
"path": "test/complex-objects.test.js",
"chars": 1045,
"preview": "'use strict'\n\nconst test = require('node:test')\nconst assert = require('node:assert')\nconst { PassThrough } = require('n"
},
{
"path": "test/crlf.test.js",
"chars": 750,
"preview": "'use strict'\n\nconst test = require('node:test')\nconst assert = require('node:assert')\n\nconst writer = require('flush-wri"
},
{
"path": "test/custom-levels.test.js",
"chars": 6108,
"preview": "'use strict'\n\n/* eslint no-prototype-builtins: 0 */\n\nconst test = require('node:test')\nconst assert = require('node:asse"
},
{
"path": "test/diagnostics.test.js",
"chars": 2961,
"preview": "'use strict'\n\nconst test = require('node:test')\nconst os = require('node:os')\nconst diagChan = require('node:diagnostics"
},
{
"path": "test/error-key.test.js",
"chars": 1155,
"preview": "'use strict'\n\nconst test = require('node:test')\nconst assert = require('node:assert')\n\nconst { sink, once } = require('."
},
{
"path": "test/error.test.js",
"chars": 8973,
"preview": "'use strict'\n\n/* eslint no-prototype-builtins: 0 */\n\nconst test = require('node:test')\nconst assert = require('node:asse"
},
{
"path": "test/escaping.test.js",
"chars": 2486,
"preview": "'use strict'\n\nconst test = require('node:test')\nconst assert = require('node:assert')\nconst os = require('node:os')\n\ncon"
},
{
"path": "test/esm/esm.mjs",
"chars": 362,
"preview": "import test from 'node:test'\nimport assert from 'node:assert'\n\nimport pino from '../../pino.js'\nimport helper from '../h"
},
{
"path": "test/esm/index.test.js",
"chars": 649,
"preview": "'use strict'\n\n// Node v8 throw a `SyntaxError: Unexpected token import`\n// even if this branch is never touched in the c"
},
{
"path": "test/esm/named-exports.mjs",
"chars": 801,
"preview": "import test from 'node:test'\nimport assert from 'node:assert'\nimport { hostname } from 'node:os'\nimport { readFileSync }"
},
{
"path": "test/exit.test.js",
"chars": 2172,
"preview": "'use strict'\n\nconst test = require('node:test')\nconst assert = require('node:assert')\nconst { join } = require('node:pat"
},
{
"path": "test/fixtures/broken-pipe/basic.js",
"chars": 251,
"preview": "'use strict'\n\nglobal.process = { __proto__: process, pid: 123456 }\nDate.now = function () { return 1459875739796 }\nrequi"
},
{
"path": "test/fixtures/broken-pipe/destination.js",
"chars": 291,
"preview": "'use strict'\n\nglobal.process = { __proto__: process, pid: 123456 }\nDate.now = function () { return 1459875739796 }\nrequi"
},
{
"path": "test/fixtures/broken-pipe/syncfalse.js",
"chars": 343,
"preview": "'use strict'\n\nglobal.process = { __proto__: process, pid: 123456 }\nDate.now = function () { return 1459875739796 }\nrequi"
},
{
"path": "test/fixtures/console-transport.js",
"chars": 319,
"preview": "const { Writable } = require('node:stream')\n\nmodule.exports = (options) => {\n const myTransportStream = new Writable({\n"
},
{
"path": "test/fixtures/crashing-transport.js",
"chars": 287,
"preview": "const { Writable } = require('node:stream')\n\nmodule.exports = () =>\n new Writable({\n autoDestroy: true,\n write (c"
},
{
"path": "test/fixtures/default-exit.js",
"chars": 305,
"preview": "global.process = { __proto__: process, pid: 123456 }\nDate.now = function () { return 1459875739796 }\nrequire('node:os')."
},
{
"path": "test/fixtures/destination-exit.js",
"chars": 328,
"preview": "global.process = { __proto__: process, pid: 123456 }\nDate.now = function () { return 1459875739796 }\nrequire('node:os')."
},
{
"path": "test/fixtures/eval/index.js",
"chars": 168,
"preview": "/* eslint-disable no-eval */\n\neval(`\nconst pino = require('../../../')\n\nconst logger = pino(\n pino.transport({\n targ"
},
{
"path": "test/fixtures/eval/node_modules/14-files.js",
"chars": 45,
"preview": "const file1 = require(\"./file1.js\")\n\nfile1()\n"
},
{
"path": "test/fixtures/eval/node_modules/2-files.js",
"chars": 48,
"preview": "const file12 = require(\"./file12.js\")\n\nfile12()\n"
},
{
"path": "test/fixtures/eval/node_modules/file1.js",
"chars": 82,
"preview": "const file2 = require(\"./file2.js\")\n\nmodule.exports = function () {\n file2()\n}\n"
},
{
"path": "test/fixtures/eval/node_modules/file10.js",
"chars": 85,
"preview": "const file11 = require(\"./file11.js\")\n\nmodule.exports = function () {\n file11()\n}\n"
},
{
"path": "test/fixtures/eval/node_modules/file11.js",
"chars": 85,
"preview": "const file12 = require(\"./file12.js\")\n\nmodule.exports = function () {\n file12()\n}\n"
},
{
"path": "test/fixtures/eval/node_modules/file12.js",
"chars": 85,
"preview": "const file13 = require(\"./file13.js\")\n\nmodule.exports = function () {\n file13()\n}\n"
},
{
"path": "test/fixtures/eval/node_modules/file13.js",
"chars": 85,
"preview": "const file14 = require(\"./file14.js\")\n\nmodule.exports = function () {\n file14()\n}\n"
},
{
"path": "test/fixtures/eval/node_modules/file14.js",
"chars": 196,
"preview": "const pino = require(\"../../../../\");\n\nmodule.exports = function() {\n const logger = pino(\n pino.transport({\n "
},
{
"path": "test/fixtures/eval/node_modules/file2.js",
"chars": 82,
"preview": "const file3 = require(\"./file3.js\")\n\nmodule.exports = function () {\n file3()\n}\n"
},
{
"path": "test/fixtures/eval/node_modules/file3.js",
"chars": 82,
"preview": "const file4 = require(\"./file4.js\")\n\nmodule.exports = function () {\n file4()\n}\n"
},
{
"path": "test/fixtures/eval/node_modules/file4.js",
"chars": 82,
"preview": "const file5 = require(\"./file5.js\")\n\nmodule.exports = function () {\n file5()\n}\n"
},
{
"path": "test/fixtures/eval/node_modules/file5.js",
"chars": 82,
"preview": "const file6 = require(\"./file6.js\")\n\nmodule.exports = function () {\n file6()\n}\n"
},
{
"path": "test/fixtures/eval/node_modules/file6.js",
"chars": 82,
"preview": "const file7 = require(\"./file7.js\")\n\nmodule.exports = function () {\n file7()\n}\n"
},
{
"path": "test/fixtures/eval/node_modules/file7.js",
"chars": 82,
"preview": "const file8 = require(\"./file8.js\")\n\nmodule.exports = function () {\n file8()\n}\n"
},
{
"path": "test/fixtures/eval/node_modules/file8.js",
"chars": 82,
"preview": "const file9 = require(\"./file9.js\")\n\nmodule.exports = function () {\n file9()\n}\n"
},
{
"path": "test/fixtures/eval/node_modules/file9.js",
"chars": 85,
"preview": "const file10 = require(\"./file10.js\")\n\nmodule.exports = function () {\n file10()\n}\n"
},
{
"path": "test/fixtures/noop-transport.js",
"chars": 170,
"preview": "const { Writable } = require('node:stream')\n\nmodule.exports = () => {\n return new Writable({\n autoDestroy: true,\n "
},
{
"path": "test/fixtures/pretty/null-prototype.js",
"chars": 358,
"preview": "global.process = { __proto__: process, pid: 123456 }\nDate.now = function () { return 1459875739796 }\nrequire('node:os')."
},
{
"path": "test/fixtures/stdout-hack-protection.js",
"chars": 368,
"preview": "global.process = { __proto__: process, pid: 123456 }\n\nconst write = process.stdout.write.bind(process.stdout)\nprocess.st"
},
{
"path": "test/fixtures/syncfalse-child.js",
"chars": 333,
"preview": "global.process = { __proto__: process, pid: 123456 }\nDate.now = function () { return 1459875739796 }\nrequire('node:os')."
},
{
"path": "test/fixtures/syncfalse-exit.js",
"chars": 386,
"preview": "global.process = { __proto__: process, pid: 123456 }\nDate.now = function () { return 1459875739796 }\nrequire('node:os')."
},
{
"path": "test/fixtures/syncfalse-flush-exit.js",
"chars": 403,
"preview": "global.process = { __proto__: process, pid: 123456 }\nDate.now = function () { return 1459875739796 }\nrequire('node:os')."
},
{
"path": "test/fixtures/syncfalse.js",
"chars": 324,
"preview": "global.process = { __proto__: process, pid: 123456 }\nDate.now = function () { return 1459875739796 }\nrequire('node:os')."
},
{
"path": "test/fixtures/syntax-error-esm.mjs",
"chars": 33,
"preview": "// This is a syntax error\nimport\n"
},
{
"path": "test/fixtures/to-file-transport-with-transform.js",
"chars": 479,
"preview": "'use strict'\n\nconst fs = require('node:fs')\nconst { once } = require('node:events')\nconst { Transform } = require('node:"
},
{
"path": "test/fixtures/to-file-transport.js",
"chars": 289,
"preview": "'use strict'\n\nconst fs = require('node:fs')\nconst { once } = require('node:events')\n\nasync function run (opts) {\n if (!"
},
{
"path": "test/fixtures/to-file-transport.mjs",
"chars": 223,
"preview": "import { createWriteStream } from 'node:fs'\nimport { once } from 'node:events'\n\nexport default async function run (opts)"
},
{
"path": "test/fixtures/transport/index.js",
"chars": 238,
"preview": "'use strict'\n\nconst fs = require('node:fs')\nconst { once } = require('node:events')\n\nasync function run (opts) {\n const"
},
{
"path": "test/fixtures/transport/package.json",
"chars": 72,
"preview": "{\n \"name\": \"transport\",\n \"version\": \"0.0.1\",\n \"main\": \"./index.js\"\n}\n"
},
{
"path": "test/fixtures/transport-exit-immediately-with-async-dest.js",
"chars": 275,
"preview": "'use strict'\n\nconst pino = require('../..')\nconst transport = pino.transport({\n target: './to-file-transport-with-trans"
},
{
"path": "test/fixtures/transport-exit-immediately.js",
"chars": 174,
"preview": "'use strict'\n\nconst pino = require('../..')\nconst transport = pino.transport({\n target: 'pino/file'\n})\nconst logger = p"
},
{
"path": "test/fixtures/transport-exit-on-ready.js",
"chars": 216,
"preview": "'use strict'\n\nconst pino = require('../..')\nconst transport = pino.transport({\n target: 'pino/file'\n})\nconst logger = p"
},
{
"path": "test/fixtures/transport-invalid-node-options.js",
"chars": 600,
"preview": "'use strict'\n\nconst pino = require('../../')\nconst { join } = require('node:path')\n\nconst destination = process.argv[2]\n"
},
{
"path": "test/fixtures/transport-main.js",
"chars": 221,
"preview": "'use strict'\n\nconst { join } = require('node:path')\nconst pino = require('../..')\nconst transport = pino.transport({\n t"
},
{
"path": "test/fixtures/transport-many-lines.js",
"chars": 462,
"preview": "'use strict'\n\nconst pino = require('../..')\nconst transport = pino.transport({\n targets: [{\n level: 'info',\n targ"
},
{
"path": "test/fixtures/transport-preload-main.mjs",
"chars": 293,
"preview": "'use strict'\n\n// This is the main script that runs after the preload\n// It imports the logger from the preload and logs "
},
{
"path": "test/fixtures/transport-preload.mjs",
"chars": 247,
"preview": "'use strict'\n\nimport pino from '../../pino.js'\nimport { join } from 'node:path'\n\nconst log = pino({\n transport: {\n t"
},
{
"path": "test/fixtures/transport-string-stdout.js",
"chars": 189,
"preview": "'use strict'\n\nconst pino = require('../..')\nconst transport = pino.transport({\n target: 'pino/file',\n options: { desti"
},
{
"path": "test/fixtures/transport-transform.js",
"chars": 535,
"preview": "'use strict'\n\nconst build = require('pino-abstract-transport')\nconst { pipeline, Transform } = require('node:stream')\nmo"
},
{
"path": "test/fixtures/transport-uses-pino-config.js",
"chars": 820,
"preview": "'use strict'\n\nconst build = require('pino-abstract-transport')\nconst { pipeline, Transform } = require('node:stream')\nmo"
},
{
"path": "test/fixtures/transport-with-on-exit.js",
"chars": 233,
"preview": "'use strict'\nconst pino = require('../..')\nconst log = pino({\n transport: {\n target: 'pino/file',\n options: { des"
},
{
"path": "test/fixtures/transport-worker-data.js",
"chars": 416,
"preview": "'use strict'\n\nconst { parentPort, workerData } = require('worker_threads')\nconst { Writable } = require('node:stream')\n\n"
},
{
"path": "test/fixtures/transport-worker-name.js",
"chars": 497,
"preview": "'use strict'\n\nconst { parentPort, threadName } = require('worker_threads')\nconst { Writable } = require('node:stream')\n\n"
},
{
"path": "test/fixtures/transport-worker.js",
"chars": 355,
"preview": "'use strict'\n\nconst { Writable } = require('node:stream')\nconst fs = require('node:fs')\nmodule.exports = (options) => {\n"
},
{
"path": "test/fixtures/transport-wrong-export-type.js",
"chars": 108,
"preview": "module.exports = {\n completelyUnrelatedProperty: 'Just a very incorrect transport worker implementation'\n}\n"
},
{
"path": "test/fixtures/ts/to-file-transport-native.mts",
"chars": 410,
"preview": "import * as fs from 'node:fs'\nimport { once } from 'node:events'\n\ninterface TransportOptions {\n destination?: fs.PathLi"
},
{
"path": "test/fixtures/ts/to-file-transport-with-transform.ts",
"chars": 504,
"preview": "import * as fs from 'node:fs'\nimport { once } from 'node:events'\nimport { Transform } from 'node:stream'\n\nasync function"
},
{
"path": "test/fixtures/ts/to-file-transport.ts",
"chars": 346,
"preview": "import * as fs from 'node:fs'\nimport { once } from 'node:events'\n\nasync function run (opts: { destination?: fs.PathLike "
},
{
"path": "test/fixtures/ts/transpile.cjs",
"chars": 1028,
"preview": "#!/usr/bin/env node\n\nconst execa = require('execa')\nconst fs = require('node:fs')\n\nconst existsSync = fs.existsSync\ncons"
},
{
"path": "test/fixtures/ts/transport-exit-immediately-with-async-dest.ts",
"chars": 307,
"preview": "import pino from '../../..'\nimport { join } from 'node:path'\n\nconst transport = pino.transport({\n target: join(__dirnam"
},
{
"path": "test/fixtures/ts/transport-exit-immediately.ts",
"chars": 159,
"preview": "import pino from '../../..'\n\nconst transport = pino.transport({\n target: 'pino/file'\n})\nconst logger = pino(transport)\n"
},
{
"path": "test/fixtures/ts/transport-exit-on-ready.ts",
"chars": 201,
"preview": "import pino from '../../..'\n\nconst transport = pino.transport({\n target: 'pino/file'\n})\nconst logger = pino(transport)\n"
},
{
"path": "test/fixtures/ts/transport-main.ts",
"chars": 201,
"preview": "import { join } from 'node:path'\nimport pino from '../../..'\n\nconst transport = pino.transport({\n target: join(__dirnam"
},
{
"path": "test/fixtures/ts/transport-string-stdout.ts",
"chars": 174,
"preview": "import pino from '../../..'\n\nconst transport = pino.transport({\n target: 'pino/file',\n options: { destination: '1' }\n}"
},
{
"path": "test/fixtures/ts/transport-worker.ts",
"chars": 286,
"preview": "import { Writable } from 'node:stream'\n\nexport default (): Writable => {\n const myTransportStream = new Writable({\n "
},
{
"path": "test/formatters.test.js",
"chars": 7300,
"preview": "'use strict'\n/* eslint no-prototype-builtins: 0 */\n\nconst test = require('node:test')\nconst assert = require('node:asser"
},
{
"path": "test/helper.d.ts",
"chars": 212,
"preview": "import type { PathLike } from 'node:fs'\n\nexport declare function watchFileCreated (filename: PathLike): Promise<void>\nex"
},
{
"path": "test/helper.js",
"chars": 3836,
"preview": "'use strict'\n\nconst crypto = require('node:crypto')\nconst { join } = require('node:path')\nconst os = require('node:os')\n"
},
{
"path": "test/hooks.test.js",
"chars": 2756,
"preview": "'use strict'\n\nconst { describe, test } = require('node:test')\nconst tspl = require('@matteo.collina/tspl')\n\nconst { sink"
},
{
"path": "test/http.test.js",
"chars": 5333,
"preview": "'use strict'\n\nconst test = require('node:test')\nconst http = require('node:http')\nconst os = require('node:os')\nconst ts"
},
{
"path": "test/internals/version.test.js",
"chars": 407,
"preview": "'use strict'\n\nconst test = require('node:test')\nconst assert = require('node:assert')\nconst fs = require('node:fs')\ncons"
},
{
"path": "test/is-level-enabled.test.js",
"chars": 6105,
"preview": "'use strict'\n\nconst { describe, test } = require('node:test')\nconst assert = require('node:assert')\n\nconst pino = requir"
},
{
"path": "test/jest/basic.spec.js",
"chars": 174,
"preview": "/* global test */\nconst pino = require('../../pino')\n\ntest('transport should work in jest', function () {\n pino({\n t"
},
{
"path": "test/levels.test.js",
"chars": 19846,
"preview": "'use strict'\n\nconst { describe, test } = require('node:test')\nconst assert = require('node:assert')\nconst tspl = require"
},
{
"path": "test/metadata.test.js",
"chars": 3061,
"preview": "'use strict'\n\nconst test = require('node:test')\nconst os = require('node:os')\nconst tspl = require('@matteo.collina/tspl"
},
{
"path": "test/mixin-merge-strategy.test.js",
"chars": 1278,
"preview": "'use strict'\n\nconst test = require('node:test')\nconst assert = require('node:assert')\n\nconst { sink, once } = require('."
},
{
"path": "test/mixin.test.js",
"chars": 5655,
"preview": "'use strict'\n\nconst test = require('node:test')\nconst assert = require('node:assert')\nconst os = require('node:os')\ncons"
},
{
"path": "test/multistream.test.js",
"chars": 16633,
"preview": "'use strict'\n\nconst test = require('node:test')\nconst assert = require('node:assert')\nconst { readFileSync } = require('"
},
{
"path": "test/redact.test.js",
"chars": 25847,
"preview": "'use strict'\n\nconst test = require('node:test')\nconst assert = require('node:assert')\n\nconst { sink, once } = require('."
},
{
"path": "test/serializers.test.js",
"chars": 7375,
"preview": "'use strict'\n\nconst test = require('node:test')\nconst assert = require('node:assert')\nconst stdSerializers = require('pi"
},
{
"path": "test/stdout-protection.test.js",
"chars": 1254,
"preview": "'use strict'\n\nconst test = require('node:test')\nconst assert = require('node:assert')\nconst { join } = require('node:pat"
},
{
"path": "test/syncfalse.test.js",
"chars": 4569,
"preview": "'use strict'\n\nconst test = require('node:test')\nconst assert = require('node:assert')\nconst os = require('node:os')\ncons"
},
{
"path": "test/timestamp-nano.test.js",
"chars": 1111,
"preview": "'use strict'\n\n/* eslint no-prototype-builtins: 0 */\n\nconst test = require('node:test')\nconst assert = require('node:asse"
},
{
"path": "test/timestamp.test.js",
"chars": 4074,
"preview": "'use strict'\n\n/* eslint no-prototype-builtins: 0 */\n\nconst test = require('node:test')\nconst assert = require('node:asse"
},
{
"path": "test/transport/big.test.js",
"chars": 1201,
"preview": "'use strict'\n\nconst test = require('node:test')\nconst assert = require('node:assert')\nconst { join } = require('node:pat"
},
{
"path": "test/transport/bundlers-support.test.js",
"chars": 2598,
"preview": "'use strict'\n\nconst test = require('node:test')\nconst assert = require('node:assert')\nconst os = require('node:os')\ncons"
},
{
"path": "test/transport/caller.test.js",
"chars": 1140,
"preview": "'use strict'\n\nconst test = require('node:test')\nconst assert = require('node:assert')\nconst { join } = require('node:pat"
},
{
"path": "test/transport/core.test.js",
"chars": 21056,
"preview": "'use strict'\n\nconst test = require('node:test')\nconst assert = require('node:assert')\nconst os = require('node:os')\ncons"
},
{
"path": "test/transport/core.transpiled.test.ts",
"chars": 3277,
"preview": "import test from 'node:test'\nimport assert from 'node:assert'\nimport * as os from 'node:os'\nimport { join } from 'node:p"
},
{
"path": "test/transport/crash.test.js",
"chars": 925,
"preview": "'use strict'\n\nconst test = require('node:test')\nconst assert = require('node:assert')\nconst { join } = require('node:pat"
},
{
"path": "test/transport/module-link.test.js",
"chars": 5428,
"preview": "'use strict'\n\nconst test = require('node:test')\nconst assert = require('node:assert')\nconst os = require('node:os')\ncons"
},
{
"path": "test/transport/native-type-stripping.test.mjs",
"chars": 4188,
"preview": "import test from 'node:test'\nimport assert from 'node:assert'\nimport * as os from 'node:os'\nimport { join } from 'node:p"
},
{
"path": "test/transport/node-options.test.js",
"chars": 3336,
"preview": "'use strict'\n\nconst test = require('node:test')\nconst assert = require('node:assert')\nconst { EventEmitter } = require('"
},
{
"path": "test/transport/pipeline.test.js",
"chars": 3832,
"preview": "'use strict'\n\nconst test = require('node:test')\nconst assert = require('node:assert')\nconst os = require('node:os')\ncons"
},
{
"path": "test/transport/preload.test.js",
"chars": 1861,
"preview": "'use strict'\n\nconst test = require('node:test')\nconst assert = require('node:assert')\nconst { join } = require('node:pat"
},
{
"path": "test/transport/repl.test.js",
"chars": 388,
"preview": "'use strict'\n\nconst test = require('node:test')\nconst assert = require('node:assert')\nconst proxyquire = require('proxyq"
},
{
"path": "test/transport/sync-false.test.js",
"chars": 1907,
"preview": "'use strict'\n\nconst test = require('node:test')\nconst assert = require('node:assert')\nconst os = require('node:os')\ncons"
},
{
"path": "test/transport/sync-true.test.js",
"chars": 1427,
"preview": "'use strict'\n\nconst test = require('node:test')\nconst assert = require('node:assert')\nconst { join } = require('node:pat"
},
{
"path": "test/transport/targets.test.js",
"chars": 1101,
"preview": "'use strict'\n\nconst test = require('node:test')\nconst { join } = require('node:path')\nconst Writable = require('node:str"
},
{
"path": "test/transport/uses-pino-config.test.js",
"chars": 3801,
"preview": "'use strict'\n\nconst test = require('node:test')\nconst assert = require('node:assert')\nconst os = require('node:os')\ncons"
},
{
"path": "test/transport-stream.test.js",
"chars": 889,
"preview": "'use strict'\n\nconst test = require('node:test')\nconst proxyquire = require('proxyquire')\nconst tspl = require('@matteo.c"
},
{
"path": "test/types/pino-import.test-d.cts",
"chars": 1170,
"preview": "import { expect } from 'tstyche'\n\nimport * as pinoStar from '../../pino.js'\nimport { type default as P, default as pino,"
},
{
"path": "test/types/pino-multistream.test-d.ts",
"chars": 1516,
"preview": "import { expect } from 'tstyche'\n\nimport { createWriteStream } from 'node:fs'\n\nimport pino, { multistream } from '../../"
},
{
"path": "test/types/pino-top-export.test-d.ts",
"chars": 837,
"preview": "import { expect } from 'tstyche'\n\nimport type { SonicBoom } from 'sonic-boom'\nimport type ThreadStream from 'thread-stre"
}
]
// ... and 7 more files (download for full content)
About this extraction
This page contains the full source code of the pinojs/pino GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 207 files (651.8 KB), approximately 181.0k tokens, and a symbol index with 371 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.