Skip to content

Commit 3477063

Browse files
authoredMay 9, 2021
Add support to ignore nested properties (#180)
* Add support to ignore nested properties * Delete from deep clone of log object * Update README * Update function description and add utils tests
1 parent 24e8ff0 commit 3477063

7 files changed

+125
-10
lines changed
 

‎Readme.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ node app.js | pino-pretty
8383
system time zone.
8484
- `--search` (`-s`): Specify a search pattern according to
8585
[jmespath](http://jmespath.org/).
86-
- `--ignore` (`-i`): Ignore one or several keys: (`-i time,hostname`)
86+
- `--ignore` (`-i`): Ignore one or several keys, nested keys are supported: (`-i time,hostname,req.headers`)
8787
- `--hideObject` (`-H`): Hide objects from output (but not error object)
8888
- `--singleLine` (`-S`): Print each log message on a single line (errors will still be multi-line)
8989
- `--config`: Specify a path to a config file containing the pino-pretty options. pino-pretty will attempt to read from a `.pino-prettyrc` in your current directory (`process.cwd`) if not specified

‎index.js

+3-7
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ const {
1111
prettifyMessage,
1212
prettifyMetadata,
1313
prettifyObject,
14-
prettifyTime
14+
prettifyTime,
15+
filterLog
1516
} = require('./lib/utils')
1617

1718
const bourne = require('@hapi/bourne')
@@ -81,12 +82,7 @@ module.exports = function prettyFactory (options) {
8182
const prettifiedMessage = prettifyMessage({ log, messageKey, colorizer, messageFormat, levelLabel })
8283

8384
if (ignoreKeys) {
84-
log = Object.keys(log)
85-
.filter(key => !ignoreKeys.has(key))
86-
.reduce((res, key) => {
87-
res[key] = log[key]
88-
return res
89-
}, {})
85+
log = filterLog(log, ignoreKeys)
9086
}
9187

9288
const prettifiedLevel = prettifyLevel({ log, colorizer, levelKey })

‎lib/utils.js

+44-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
'use strict'
22

3+
const clone = require('rfdc')()
34
const dateformat = require('dateformat')
45
const stringifySafe = require('fast-safe-stringify')
56
const defaultColorizer = require('./colors')()
@@ -21,13 +22,15 @@ module.exports = {
2122
prettifyMessage,
2223
prettifyMetadata,
2324
prettifyObject,
24-
prettifyTime
25+
prettifyTime,
26+
filterLog
2527
}
2628

2729
module.exports.internals = {
2830
formatTime,
2931
joinLinesWithIndentation,
30-
prettifyError
32+
prettifyError,
33+
deleteLogProperty
3134
}
3235

3336
/**
@@ -435,3 +438,42 @@ function prettifyError ({ keyName, lines, eol, ident }) {
435438

436439
return result
437440
}
441+
442+
/**
443+
* Deletes a specified property from a log object if it exists.
444+
* This function mutates the passed in `log` object.
445+
*
446+
* @param {object} log The log object to be modified.
447+
* @param {string} property A string identifying the property to be deleted from
448+
* the log object. Accepts nested properties delimited by a `.`
449+
* e.g. `'prop1.prop2'`.
450+
*/
451+
function deleteLogProperty (log, property) {
452+
const props = property.split('.')
453+
const propToDelete = props.pop()
454+
455+
props.forEach((prop) => {
456+
if (!Object.prototype.hasOwnProperty.call(log, prop)) {
457+
return
458+
}
459+
log = log[prop]
460+
})
461+
462+
delete log[propToDelete]
463+
}
464+
465+
/**
466+
* Filter a log object by removing any ignored keys.
467+
*
468+
* @param {object} log The log object to be modified.
469+
* @param {string} ignoreKeys An array of strings identifying the properties to be removed.
470+
*
471+
* @returns {object} A new `log` object instance that does not include the ignored keys.
472+
*/
473+
function filterLog (log, ignoreKeys) {
474+
const logCopy = clone(log)
475+
ignoreKeys.forEach((ignoreKey) => {
476+
deleteLogProperty(logCopy, ignoreKey)
477+
})
478+
return logCopy
479+
}

‎package.json

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"joycon": "^2.2.5",
3939
"pump": "^3.0.0",
4040
"readable-stream": "^3.6.0",
41+
"rfdc": "^1.3.0",
4142
"split2": "^3.1.1",
4243
"strip-json-comments": "^3.1.1"
4344
},

‎test/cli.test.js

+23
Original file line numberDiff line numberDiff line change
@@ -133,5 +133,28 @@ test('cli', (t) => {
133133
t.tearDown(() => child.kill())
134134
})
135135

136+
t.test('does ignore nested keys', (t) => {
137+
t.plan(1)
138+
139+
const logLineNested = JSON.stringify(Object.assign(JSON.parse(logLine), {
140+
extra: {
141+
foo: 'bar',
142+
number: 42,
143+
nested: {
144+
foo2: 'bar2'
145+
}
146+
}
147+
})) + '\n'
148+
149+
const env = { TERM: 'dumb' }
150+
const child = spawn(process.argv[0], [bin, '-S', '-i', 'extra.foo,extra.nested,extra.nested.miss'], { env })
151+
child.on('error', t.threw)
152+
child.stdout.on('data', (data) => {
153+
t.is(data.toString(), `[${epoch}] INFO (42 on foo): hello world {"extra":{"number":42}}\n`)
154+
})
155+
child.stdin.write(logLineNested)
156+
t.tearDown(() => child.kill())
157+
})
158+
136159
t.end()
137160
})

‎test/lib/utils.internals.test.js

+30
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict'
22

33
const tap = require('tap')
4+
const clone = require('rfdc')()
45
const stringifySafe = require('fast-safe-stringify')
56
const { internals } = require('../../lib/utils')
67

@@ -95,3 +96,32 @@ tap.test('#prettifyError', t => {
9596

9697
t.end()
9798
})
99+
100+
tap.test('#deleteLogProperty', t => {
101+
const logData = {
102+
level: 30,
103+
data1: {
104+
data2: { 'data-3': 'bar' }
105+
}
106+
}
107+
108+
t.test('deleteLogProperty deletes property of depth 1', async t => {
109+
const log = clone(logData)
110+
internals.deleteLogProperty(log, 'data1')
111+
t.same(log, { level: 30 })
112+
})
113+
114+
t.test('deleteLogProperty deletes property of depth 2', async t => {
115+
const log = clone(logData)
116+
internals.deleteLogProperty(log, 'data1.data2')
117+
t.same(log, { level: 30, data1: { } })
118+
})
119+
120+
t.test('deleteLogProperty deletes property of depth 3', async t => {
121+
const log = clone(logData)
122+
internals.deleteLogProperty(log, 'data1.data2.data-3')
123+
t.same(log, { level: 30, data1: { data2: { } } })
124+
})
125+
126+
t.end()
127+
})

‎test/lib/utils.public.test.js

+23
Original file line numberDiff line numberDiff line change
@@ -341,3 +341,26 @@ tap.test('prettifyTime', t => {
341341

342342
t.end()
343343
})
344+
345+
tap.test('#filterLog', t => {
346+
const { filterLog } = utils
347+
const logData = {
348+
level: 30,
349+
time: 1522431328992,
350+
data1: {
351+
data2: { 'data-3': 'bar' }
352+
}
353+
}
354+
355+
t.test('filterLog removes single entry', async t => {
356+
const result = filterLog(logData, ['data1.data2.data-3'])
357+
t.same(result, { level: 30, time: 1522431328992, data1: { data2: { } } })
358+
})
359+
360+
t.test('filterLog removes multiple entries', async t => {
361+
const result = filterLog(logData, ['time', 'data1'])
362+
t.same(result, { level: 30 })
363+
})
364+
365+
t.end()
366+
})

0 commit comments

Comments
 (0)
Please sign in to comment.