Skip to content

Commit

Permalink
Merge 9e50ad1 into fad9481
Browse files Browse the repository at this point in the history
  • Loading branch information
nikrabaev committed Mar 4, 2023
2 parents fad9481 + 9e50ad1 commit 343badd
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 34 deletions.
4 changes: 3 additions & 1 deletion Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ node app.js | pino-pretty
error like objects. Default: `err,error`.
- `--messageKey` (`-m`): Define the key that contains the main log message.
Default: `msg`.
- `--levelKey` (`--levelKey`): Define the key that contains the level of the log.
- `--levelKey` (`--levelKey`): Define the key that contains the level of the log, nested keys are supported with each property delimited by a dot character (`.`),
keys may be escaped to target property names that contains the delimiter itself:
(`--levelKey tags\\.level`).
Default: `level`.
- `--levelLabel` (`-b`): Output the log level using the specified label.
Default: `levelLabel`.
Expand Down
61 changes: 39 additions & 22 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ module.exports.internals = {
formatTime,
joinLinesWithIndentation,
prettifyError,
getPropertyValue,
deleteLogProperty,
splitIgnoreKey,
splitPropertyKey,
createDate,
isValidDate
}
Expand Down Expand Up @@ -235,8 +236,8 @@ function prettifyErrorLog ({
* `colorizer` is returned.
*/
function prettifyLevel ({ log, colorizer = defaultColorizer, levelKey = LEVEL_KEY, prettifier, customLevels, customLevelNames }) {
if (levelKey in log === false) return undefined
const output = log[levelKey]
const output = getPropertyValue(log, levelKey)
if (output === undefined) return undefined
return prettifier ? prettifier(output) : colorizer(output, { customLevels, customLevelNames })
}

Expand Down Expand Up @@ -264,17 +265,13 @@ function prettifyMessage ({ log, messageFormat, messageKey = MESSAGE_KEY, colori
if (messageFormat && typeof messageFormat === 'string') {
const message = String(messageFormat).replace(/{([^{}]+)}/g, function (match, p1) {
// return log level as string instead of int
if (p1 === levelLabel && log[levelKey]) {
const condition = useOnlyCustomProps ? customLevels === undefined : customLevels[log[levelKey]] === undefined
return condition ? LEVELS[log[levelKey]] : customLevels[log[levelKey]]
let level
if (p1 === levelLabel && (level = getPropertyValue(log, levelKey)) !== undefined) {
const condition = useOnlyCustomProps ? customLevels === undefined : customLevels[level] === undefined
return condition ? LEVELS[level] : customLevels[level]
}
// Parse nested key access, e.g. `{keyA.subKeyB}`.
return p1.split('.').reduce(function (prev, curr) {
if (prev && prev[curr]) {
return prev[curr]
}
return ''
}, log)
return getPropertyValue(log, p1) || ''
})
return colorizer.message(message)
}
Expand Down Expand Up @@ -510,15 +507,15 @@ function prettifyError ({ keyName, lines, eol, ident }) {
}

/**
* Splits the input key delimited by a dot character but not when it is preceded
* Splits the property key delimited by a dot character but not when it is preceded
* by a backslash.
*
* @param {string} key A string identifying the property.
*
* @returns {string[]} Returns a list of string containing each delimited property.
* e.g. `'prop2\.domain\.corp.prop2'` should return [ 'prop2.domain.com', 'prop2' ]
*/
function splitIgnoreKey (key) {
function splitPropertyKey (key) {
const result = []
let backslash = false
let segment = ''
Expand Down Expand Up @@ -555,6 +552,29 @@ function splitIgnoreKey (key) {
return result
}

/**
* Gets a specified property from an object if it exists.
* @param {object} obj The object to be searched.
* @param {(string|string[])} property A string or an array of strings identifying
* the property to be retrieved from the object.
* Accepts nested properties delimited by a `.`
* Delimiter can be escaped to preserve property names that contain the delimiter.
* e.g. `'prop1.prop2'` or `'prop2\.domain\.corp.prop2'`
* @returns {*}
*/
function getPropertyValue (obj, property) {
const props = Array.isArray(property) ? property : splitPropertyKey(property)

for (const prop of props) {
if (!Object.prototype.hasOwnProperty.call(obj, prop)) {
return
}
obj = obj[prop]
}

return obj
}

/**
* Deletes a specified property from a log object if it exists.
* This function mutates the passed in `log` object.
Expand All @@ -566,17 +586,14 @@ function splitIgnoreKey (key) {
* e.g. `'prop1.prop2'` or `'prop2\.domain\.corp.prop2'`
*/
function deleteLogProperty (log, property) {
const props = splitIgnoreKey(property)
const props = splitPropertyKey(property)
const propToDelete = props.pop()

props.forEach((prop) => {
if (!Object.prototype.hasOwnProperty.call(log, prop)) {
return
}
log = log[prop]
})
log = getPropertyValue(log, props)

delete log[propToDelete]
if (log !== null && typeof log === 'object' && Object.prototype.hasOwnProperty.call(log, propToDelete)) {
delete log[propToDelete]
}
}

/**
Expand Down
53 changes: 42 additions & 11 deletions test/lib/utils.internals.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,31 +163,62 @@ tap.test('#deleteLogProperty', t => {
t.end()
})

tap.test('#splitIgnoreKey', t => {
t.test('splitIgnoreKey does not change key', async t => {
const result = internals.splitIgnoreKey('data1')
tap.test('#splitPropertyKey', t => {
t.test('splitPropertyKey does not change key', async t => {
const result = internals.splitPropertyKey('data1')
t.same(result, ['data1'])
})

t.test('splitIgnoreKey splits nested key', async t => {
const result = internals.splitIgnoreKey('data1.data2.data-3')
t.test('splitPropertyKey splits nested key', async t => {
const result = internals.splitPropertyKey('data1.data2.data-3')
t.same(result, ['data1', 'data2', 'data-3'])
})

t.test('splitIgnoreKey splits nested keys ending with a dot', async t => {
const result = internals.splitIgnoreKey('data1.data2.data-3.')
t.test('splitPropertyKey splits nested keys ending with a dot', async t => {
const result = internals.splitPropertyKey('data1.data2.data-3.')
t.same(result, ['data1', 'data2', 'data-3'])
})

t.test('splitIgnoreKey splits nested escaped key', async t => {
const result = internals.splitIgnoreKey('logging\\.domain\\.corp/operation.foo.bar-2')
t.test('splitPropertyKey splits nested escaped key', async t => {
const result = internals.splitPropertyKey('logging\\.domain\\.corp/operation.foo.bar-2')
t.same(result, ['logging.domain.corp/operation', 'foo', 'bar-2'])
})

t.test('splitIgnoreKey splits nested escaped key with special characters', async t => {
const result = internals.splitIgnoreKey('logging\\.domain\\.corp/operation.!\t@#$%^&*()_+=-<>.bar\\.2')
t.test('splitPropertyKey splits nested escaped key with special characters', async t => {
const result = internals.splitPropertyKey('logging\\.domain\\.corp/operation.!\t@#$%^&*()_+=-<>.bar\\.2')
t.same(result, ['logging.domain.corp/operation', '!\t@#$%^&*()_+=-<>', 'bar.2'])
})

t.end()
})

tap.test('#getPropertyValue', t => {
t.test('getPropertyValue returns the value of the property', async t => {
const result = internals.getPropertyValue({
foo: 'bar'
}, 'foo')
t.same(result, 'bar')
})

t.test('getPropertyValue returns the value of the nested property', async t => {
const result = internals.getPropertyValue({ extra: { foo: { value: 'bar' } } }, 'extra.foo.value')
t.same(result, 'bar')
})

t.test('getPropertyValue returns the value of the nested property using the array of nested property keys', async t => {
const result = internals.getPropertyValue({ extra: { foo: { value: 'bar' } } }, ['extra', 'foo', 'value'])
t.same(result, 'bar')
})

t.test('getPropertyValue returns undefined for non-existing properties', async t => {
const result = internals.getPropertyValue({ extra: { foo: { value: 'bar' } } }, 'extra.foo.value-2')
t.same(result, undefined)
})

t.test('getPropertyValue returns undefined for non-existing properties using the array of nested property keys', async t => {
const result = internals.getPropertyValue({ extra: { foo: { value: 'bar' } } }, ['extra', 'foo', 'value-2'])
t.same(result, undefined)
})

t.end()
})

0 comments on commit 343badd

Please sign in to comment.