Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: automatically consume pino config when using pino@>=8.21.0 #508

Merged
merged 1 commit into from
Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,14 @@ jobs:
contents: read
strategy:
matrix:
node-version: [14, 16, 18]
node-version: [14, 16, 18, 20]
os: [ubuntu-latest]
pino-version: [8.20.0, ^8.21.0, ^9.0.0]
exclude:
- node-version: 14
pino-version: ^9.0.0
- node-version: 16
pino-version: ^9.0.0
steps:
- name: Check out repo
uses: actions/checkout@v4
Expand All @@ -62,6 +68,9 @@ jobs:
- name: Install dependencies
run: npm i --ignore-scripts

- name: Install pino ${{ matrix.pino-version }}
run: npm i --no-save pino@${{ matrix.pino-version }}

- name: Run tests
run: npm run ci

Expand Down
6 changes: 3 additions & 3 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,10 +240,10 @@ The options accepted have keys corresponding to the options described in [CLI Ar
colorize: colorette.isColorSupported, // --colorize
colorizeObjects: true, //--colorizeObjects
crlf: false, // --crlf
errorLikeObjectKeys: ['err', 'error'], // --errorLikeObjectKeys
errorLikeObjectKeys: ['err', 'error'], // --errorLikeObjectKeys (not required to match custom errorKey with pino >=8.21.0)
errorProps: '', // --errorProps
levelFirst: false, // --levelFirst
messageKey: 'msg', // --messageKey
messageKey: 'msg', // --messageKey (not required with pino >=8.21.0)
levelKey: 'level', // --levelKey
messageFormat: false, // --messageFormat
timestampKey: 'time', // --timestampKey
Expand All @@ -253,7 +253,7 @@ The options accepted have keys corresponding to the options described in [CLI Ar
hideObject: false, // --hideObject
singleLine: false, // --singleLine
customColors: 'err:red,info:blue', // --customColors
customLevels: 'err:99,info:1', // --customLevels
customLevels: 'err:99,info:1', // --customLevels (not required with pino >=8.21.0)
levelLabel: 'levelLabel', // --levelLabel
minimumLevel: 'info', // --minimumLevel
useOnlyCustomProps: true, // --useOnlyCustomProps
Expand Down
6 changes: 6 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ interface PrettyOptions_ {
/**
* The key in the JSON object to use as the highlighted message.
* @default "msg"
*
* Not required when used with pino >= 8.21.0
*/
messageKey?: string;
/**
Expand Down Expand Up @@ -122,6 +124,8 @@ interface PrettyOptions_ {
/**
* Define the log keys that are associated with error like objects.
* @default ["err", "error"]
*
* Not required to handle custom errorKey when used with pino >= 8.21.0
*/
errorLikeObjectKeys?: string[];
/**
Expand Down Expand Up @@ -192,6 +196,8 @@ interface PrettyOptions_ {
*
* @example ( CSV ) customLevels: 'info:10,some_level:40'
* @example ( Object ) customLevels: { info: 10, some_level: 40 }
*
* Not required when used with pino >= 8.21.0
*/
customLevels?: string|object;
/**
Expand Down
12 changes: 11 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,18 @@ function prettyFactory (options) {
* @returns {Transform | (Transform & OnUnknown)}
*/
function build (opts = {}) {
const pretty = prettyFactory(opts)
let pretty = prettyFactory(opts)
return abstractTransport(function (source) {
source.on('message', function pinoConfigListener (message) {
if (!message || message.code !== 'PINO_CONFIG') return
Object.assign(opts, {
messageKey: message.config.messageKey,
errorLikeObjectKeys: Array.from(new Set([...(opts.errorLikeObjectKeys || ERROR_LIKE_KEYS), message.config.errorKey])),
customLevels: message.config.levels.values
})
pretty = prettyFactory(opts)
source.off('message', pinoConfigListener)
})
const stream = new Transform({
objectMode: true,
autoDestroy: true,
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"pino": "^8.0.0",
"pre-commit": "^1.2.2",
"rimraf": "^3.0.2",
"semver": "^7.6.0",
"snazzy": "^9.0.0",
"standard": "^17.0.0",
"tap": "^16.0.0",
Expand Down
86 changes: 71 additions & 15 deletions test/basic.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const path = require('path')
const rimraf = require('rimraf')
const { join } = require('path')
const fs = require('fs')
const semver = require('semver')
const pinoPretty = require('..')
const SonicBoom = require('sonic-boom')
const _prettyFactory = pinoPretty.prettyFactory
Expand Down Expand Up @@ -1074,7 +1075,7 @@ test('basic prettifier tests', (t) => {
t.test('stream usage', async (t) => {
t.plan(1)
const tmpDir = path.join(__dirname, '.tmp_' + Date.now())
t.teardown(() => rimraf(tmpDir, noop))
t.teardown(() => rimraf.sync(tmpDir))

const destination = join(tmpDir, 'output')

Expand Down Expand Up @@ -1102,24 +1103,28 @@ test('basic prettifier tests', (t) => {
t.test('sync option', async (t) => {
t.plan(1)
const tmpDir = path.join(__dirname, '.tmp_' + Date.now())
t.teardown(() => rimraf(tmpDir, noop))
t.teardown(() => rimraf.sync(tmpDir))

const destination = join(tmpDir, 'output')

const pretty = pinoPretty({
singleLine: true,
colorize: false,
mkdir: true,
append: false,
sync: true,
destination
})
const log = pino(pretty)
log.info({ msg: 'message', extra: { foo: 'bar', number: 42 }, upper: 'foobar' })
const log = pino(pino.transport({
target: '..',
options: {
singleLine: true,
colorize: false,
mkdir: true,
append: false,
sync: true,
destination
}
}))
log.info({ msg: 'message', extra: { foo: 'bar', number: 43 }, upper: 'foobar' })

await watchFileCreated(destination)

const formatted = fs.readFileSync(destination, 'utf8')

t.equal(formatted, `[${formattedEpoch}] INFO (${pid}): message {"extra":{"foo":"bar","number":42},"upper":"foobar"}\n`)
t.equal(formatted, `[${formattedEpoch}] INFO (${pid}): message {"extra":{"foo":"bar","number":43},"upper":"foobar"}\n`)
})

t.test('support custom colors object', async (t) => {
Expand Down Expand Up @@ -1151,6 +1156,59 @@ test('basic prettifier tests', (t) => {
t.end()
})

if (semver.gte(pino.version, '8.21.0')) {
test('using pino config', (t) => {
t.beforeEach(() => {
Date.originalNow = Date.now
Date.now = () => epoch
})
t.afterEach(() => {
Date.now = Date.originalNow
delete Date.originalNow
})

t.test('can use different message keys', (t) => {
t.plan(1)
const destination = new Writable({
write (formatted, enc, cb) {
t.equal(
formatted.toString(),
`[${formattedEpoch}] INFO (${pid}): baz\n`
)
cb()
}
})
const pretty = pinoPretty({
destination,
colorize: false
})
const log = pino({ messageKey: 'bar' }, pretty)
log.info({ bar: 'baz' })
})

t.test('handles customLogLevels', (t) => {
t.plan(1)
const destination = new Writable({
write (formatted, enc, cb) {
t.equal(
formatted.toString(),
`[${formattedEpoch}] TESTCUSTOM (${pid}): test message\n`
)
cb()
}
})
const pretty = pinoPretty({
destination,
colorize: false
})
const log = pino({ customLevels: { testCustom: 35 } }, pretty)
log.testCustom('test message')
})

t.end()
})
}

function watchFileCreated (filename) {
return new Promise((resolve, reject) => {
const TIMEOUT = 2000
Expand All @@ -1171,5 +1229,3 @@ function watchFileCreated (filename) {
}, INTERVAL)
})
}

function noop () {}
47 changes: 46 additions & 1 deletion test/error-objects.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ process.env.TZ = 'UTC'
const Writable = require('stream').Writable
const test = require('tap').test
const pino = require('pino')
const semver = require('semver')
const serializers = pino.stdSerializers
const _prettyFactory = require('../').prettyFactory
const pinoPretty = require('../')
const _prettyFactory = pinoPretty.prettyFactory

function prettyFactory (opts) {
if (!opts) {
Expand Down Expand Up @@ -452,3 +454,46 @@ test('error like objects tests', (t) => {

t.end()
})

if (semver.gte(pino.version, '8.21.0')) {
test('using pino config', (t) => {
t.beforeEach(() => {
Date.originalNow = Date.now
Date.now = () => epoch
})
t.afterEach(() => {
Date.now = Date.originalNow
delete Date.originalNow
})

t.test('prettifies Error in custom errorKey', (t) => {
t.plan(8)
const destination = new Writable({
write (chunk, enc, cb) {
const formatted = chunk.toString()
const lines = formatted.split('\n')
t.equal(lines.length, expected.length + 7)
t.equal(lines[0], `[${formattedEpoch}] INFO (${pid}): hello world`)
t.match(lines[1], /\s{4}customErrorKey: {/)
t.match(lines[2], /\s{6}"type": "Error",/)
t.match(lines[3], /\s{6}"message": "hello world",/)
t.match(lines[4], /\s{6}"stack":/)
t.match(lines[5], /\s{6}Error: hello world/)
// Node 12 labels the test `<anonymous>`
t.match(lines[6], /\s{10}(at Test.t.test|at Test.<anonymous>)/)
cb()
}
})
const pretty = pinoPretty({
destination,
colorize: false
})
const log = pino({ errorKey: 'customErrorKey' }, pretty)
const err = Error('hello world')
const expected = err.stack.split('\n')
log.info({ customErrorKey: err })
})

t.end()
})
}