From 39feab1ccfb8884d21d015cf83a219ee8b7398fb Mon Sep 17 00:00:00 2001 From: timlohse1104 Date: Sun, 23 Jul 2023 21:16:30 +0200 Subject: [PATCH 1/8] Add working happy path for if / end block conditions --- lib/utils.js | 61 ++++++++++++++++++++++++++++------- test/lib/utils.public.test.js | 5 +++ 2 files changed, 54 insertions(+), 12 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index db3536ba..9b79d5df 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -263,16 +263,53 @@ function prettifyLevel ({ log, colorizer = defaultColorizer, levelKey = LEVEL_KE */ function prettifyMessage ({ log, messageFormat, messageKey = MESSAGE_KEY, colorizer = defaultColorizer, levelLabel = LEVEL_LABEL, levelKey = LEVEL_KEY, customLevels, useOnlyCustomProps }) { if (messageFormat && typeof messageFormat === 'string') { - const message = String(messageFormat).replace(/{([^{}]+)}/g, function (match, p1) { - // return log level as string instead of int - 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 getPropertyValue(log, p1) || '' - }) + // console.log( + // "pino-pretty | prettifyMessage | messageFormat: ", + // messageFormat + // ); + // console.log( + // "pino-pretty | prettifyMessage | typeof messageFormat: ", + // typeof messageFormat + // ); + // console.log("pino-pretty | prettifyMessage | log: ", log); + // console.log("pino-pretty | prettifyMessage | typeof log: ", typeof log); + + const parseMessageFormat = (input, log) => { + input = input.replace(/{if (.*?)}(.*?){end}/g, function (_, key, value) { + // console.log(`_: "${_}"`); + // console.log(`key: "${key}"`); + // console.log(`value: "${value}"`); + const propertyValue = getPropertyValue(log, key) + if (propertyValue) { + return value.replace(new RegExp('{' + key + '}', 'g'), propertyValue) + } else { + return '' + } + }) + + // console.log(`input: "${input}"`); + const replacedInput = input.replace(/\s+/g, ' ').trim() + // console.log(`replacedInput: "${replacedInput}"`); + return replacedInput + } + + const parsedMessageFormat = parseMessageFormat(messageFormat, log) + // console.log("parsedMessageFormat", parsedMessageFormat); + + const message = String(parsedMessageFormat).replace( + /{([^{}]+)}/g, + function (match, p1) { + // return log level as string instead of int + let level + if (p1 === levelLabel && (level = getPropertyValue(log, levelKey)) !== undefined) { + const condition = useOnlyCustomProps ? customLevels === undefined : customLevels[level] === undefined + return condition ? LEVELS[level] : customLevels[level] + } + // console.log('pino-pretty | prettifyMessage | p1: ', p1) + + // Parse nested key access, e.g. `{keyA.subKeyB}`. + return getPropertyValue(log, p1) || '' + }) return colorizer.message(message) } if (messageFormat && typeof messageFormat === 'function') { @@ -383,7 +420,7 @@ function prettifyObject ({ // Split object keys into two categories: error and non-error const { plain, errors } = Object.entries(input).reduce(({ plain, errors }, [k, v]) => { if (keysToIgnore.includes(k) === false) { - // Pre-apply custom prettifiers, because all 3 cases below will need this + // Pre-apply custom prettifiers, because all 3 cases below will need this const pretty = typeof customPrettifiers[k] === 'function' ? customPrettifiers[k](v, k, input) : v @@ -557,7 +594,7 @@ function splitPropertyKey (key) { * * @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. + * 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'`. diff --git a/test/lib/utils.public.test.js b/test/lib/utils.public.test.js index c6b20e2c..068af254 100644 --- a/test/lib/utils.public.test.js +++ b/test/lib/utils.public.test.js @@ -141,6 +141,11 @@ tap.test('prettifyMessage', t => { t.equal(str, 'localhost/test - param: - foo') }) + t.test('`messageFormat` supports conditional blocks', async t => { + const str = prettifyMessage({ log: { level: 30, req: { id: 'foo' } }, messageFormat: '{level} | {if req.id}({req.id}){end}{if msg}{msg}{end}' }) + t.equal(str, '30 | (foo)') + }) + t.test('`messageFormat` supports function definition', async t => { const str = prettifyMessage({ log: { level: 30, request: { url: 'localhost/test' }, msg: 'incoming request' }, From 2acc0fe646afe2111a9eecd89e33dd642272862e Mon Sep 17 00:00:00 2001 From: timlohse1104 Date: Sun, 23 Jul 2023 22:14:48 +0200 Subject: [PATCH 2/8] Refactor parsing of messageFormat conditionals in a separate function --- lib/utils.js | 64 ++++++++++++++++---------------- test/lib/utils.internals.test.js | 26 +++++++++++++ 2 files changed, 57 insertions(+), 33 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index 9b79d5df..9ad71c13 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -42,7 +42,8 @@ module.exports.internals = { deleteLogProperty, splitPropertyKey, createDate, - isValidDate + isValidDate, + parseMessageFormat } /** @@ -263,38 +264,7 @@ function prettifyLevel ({ log, colorizer = defaultColorizer, levelKey = LEVEL_KE */ function prettifyMessage ({ log, messageFormat, messageKey = MESSAGE_KEY, colorizer = defaultColorizer, levelLabel = LEVEL_LABEL, levelKey = LEVEL_KEY, customLevels, useOnlyCustomProps }) { if (messageFormat && typeof messageFormat === 'string') { - // console.log( - // "pino-pretty | prettifyMessage | messageFormat: ", - // messageFormat - // ); - // console.log( - // "pino-pretty | prettifyMessage | typeof messageFormat: ", - // typeof messageFormat - // ); - // console.log("pino-pretty | prettifyMessage | log: ", log); - // console.log("pino-pretty | prettifyMessage | typeof log: ", typeof log); - - const parseMessageFormat = (input, log) => { - input = input.replace(/{if (.*?)}(.*?){end}/g, function (_, key, value) { - // console.log(`_: "${_}"`); - // console.log(`key: "${key}"`); - // console.log(`value: "${value}"`); - const propertyValue = getPropertyValue(log, key) - if (propertyValue) { - return value.replace(new RegExp('{' + key + '}', 'g'), propertyValue) - } else { - return '' - } - }) - - // console.log(`input: "${input}"`); - const replacedInput = input.replace(/\s+/g, ' ').trim() - // console.log(`replacedInput: "${replacedInput}"`); - return replacedInput - } - const parsedMessageFormat = parseMessageFormat(messageFormat, log) - // console.log("parsedMessageFormat", parsedMessageFormat); const message = String(parsedMessageFormat).replace( /{([^{}]+)}/g, @@ -305,7 +275,6 @@ function prettifyMessage ({ log, messageFormat, messageKey = MESSAGE_KEY, colori const condition = useOnlyCustomProps ? customLevels === undefined : customLevels[level] === undefined return condition ? LEVELS[level] : customLevels[level] } - // console.log('pino-pretty | prettifyMessage | p1: ', p1) // Parse nested key access, e.g. `{keyA.subKeyB}`. return getPropertyValue(log, p1) || '' @@ -803,3 +772,32 @@ function handleCustomlevelNamesOpts (cLevels) { return {} } } + +/** + * Translates all conditional blocks from within the messageFormat. Translates any matching + * {if key}{key}{end} statements and returns everything between if and else blocks if the key provided + * was found in log. + * + * @param {string} messageFormat A format string or function that defines how the + * logged message should be conditionally formatted, e.g. `'{if level}{level}{end} - {if req.id}{req.id}{end}'`. + * @param {object} log The log object to be modified. + * + * @returns {string} The parsed messageFormat. + */ +function parseMessageFormat (messageFormat, log) { + messageFormat = messageFormat.replace(/{if (.*?)}(.*?){end}/g, function (_, key, value) { + const propertyValue = getPropertyValue(log, key) + if (propertyValue) { + return value.replace(new RegExp('{' + key + '}', 'g'), propertyValue) + } else { + return '' + } + }) + + // Remove unescaped if blocks + messageFormat = messageFormat.replace(/{if (.*?)}/g, '') + // Remove unescaped end blocks + messageFormat = messageFormat.replace(/{end}/g, '') + + return messageFormat.replace(/\s+/g, ' ').trim() +} diff --git a/test/lib/utils.internals.test.js b/test/lib/utils.internals.test.js index a0c2ea91..4dbb70cb 100644 --- a/test/lib/utils.internals.test.js +++ b/test/lib/utils.internals.test.js @@ -222,3 +222,29 @@ tap.test('#getPropertyValue', t => { t.end() }) + +tap.test('#parseMessageFormat', t => { + const logData = { + level: 30, + data1: { + data2: 'bar' + } + } + + t.test('parseMessageFormat translates if / else statement to found property value', async t => { + const log = fastCopy(logData) + t.equal(internals.parseMessageFormat('{level} - {if data1.data2}{data1.data2}{end}', log), '{level} - bar') + }) + + t.test('parseMessageFormat removes unescaped if statements', async t => { + const log = fastCopy(logData) + t.equal(internals.parseMessageFormat('{level} - {if data1.data2}{data1.data2}', log), '{level} - {data1.data2}') + }) + + t.test('parseMessageFormat removes unescaped end statements', async t => { + const log = fastCopy(logData) + t.equal(internals.parseMessageFormat('{level} - {data1.data2}{end}', log), '{level} - {data1.data2}') + }) + + t.end() +}) From 78578c517ff55ec4167050b2b626c31b43bee8d3 Mon Sep 17 00:00:00 2001 From: timlohse1104 Date: Mon, 24 Jul 2023 11:28:50 +0200 Subject: [PATCH 3/8] Add documentation for conditions in readme, add matching between condition and property key and add further unit tests for parseMessageFormat function --- Readme.md | 8 +++++++ lib/utils.js | 2 +- test/lib/utils.internals.test.js | 38 +++++++++++++++++++++++++++++++- 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/Readme.md b/Readme.md index d39082ba..528c0738 100644 --- a/Readme.md +++ b/Readme.md @@ -343,6 +343,14 @@ const levelPrettifier = logLevel => `LEVEL: ${levelColorize(logLevel)}` } ``` +In addition to this, if / end statement blocks can also be specified. Else statements and nested conditions are not supported. + +```js +{ + messageFormat: '{levelLabel} - {if pid}{pid}{end} - url:{req.url}' +} +``` + This option can also be defined as a `function` with this prototype: ```js diff --git a/lib/utils.js b/lib/utils.js index 9ad71c13..d585516b 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -787,7 +787,7 @@ function handleCustomlevelNamesOpts (cLevels) { function parseMessageFormat (messageFormat, log) { messageFormat = messageFormat.replace(/{if (.*?)}(.*?){end}/g, function (_, key, value) { const propertyValue = getPropertyValue(log, key) - if (propertyValue) { + if (propertyValue && value.includes(key)) { return value.replace(new RegExp('{' + key + '}', 'g'), propertyValue) } else { return '' diff --git a/test/lib/utils.internals.test.js b/test/lib/utils.internals.test.js index 4dbb70cb..ed3161fc 100644 --- a/test/lib/utils.internals.test.js +++ b/test/lib/utils.internals.test.js @@ -228,7 +228,8 @@ tap.test('#parseMessageFormat', t => { level: 30, data1: { data2: 'bar' - } + }, + msg: 'foo' } t.test('parseMessageFormat translates if / else statement to found property value', async t => { @@ -236,6 +237,11 @@ tap.test('#parseMessageFormat', t => { t.equal(internals.parseMessageFormat('{level} - {if data1.data2}{data1.data2}{end}', log), '{level} - bar') }) + t.test('parseMessageFormat translates if / else statement to found property value and leave unmatched property key untouched', async t => { + const log = fastCopy(logData) + t.equal(internals.parseMessageFormat('{level} - {if data1.data2}{data1.data2} ({msg}){end}', log), '{level} - bar ({msg})') + }) + t.test('parseMessageFormat removes unescaped if statements', async t => { const log = fastCopy(logData) t.equal(internals.parseMessageFormat('{level} - {if data1.data2}{data1.data2}', log), '{level} - {data1.data2}') @@ -246,5 +252,35 @@ tap.test('#parseMessageFormat', t => { t.equal(internals.parseMessageFormat('{level} - {data1.data2}{end}', log), '{level} - {data1.data2}') }) + t.test('parseMessageFormat removes if / end blocks if existent condition key does not match existent property key', async t => { + const log = fastCopy(logData) + t.equal(internals.parseMessageFormat('{level}{if msg}{data1.data2}{end}', log), '{level}') + }) + + t.test('parseMessageFormat removes if / end blocks if non-existent condition key does not match existent property key', async t => { + const log = fastCopy(logData) + t.equal(internals.parseMessageFormat('{level}{if foo}{msg}{end}', log), '{level}') + }) + + t.test('parseMessageFormat removes if / end blocks if existent condition key does not match non-existent property key', async t => { + const log = fastCopy(logData) + t.equal(internals.parseMessageFormat('{level}{if msg}{foo}{end}', log), '{level}') + }) + + t.test('parseMessageFormat removes if / end blocks if non-existent condition key does not match non-existent property key', async t => { + const log = fastCopy(logData) + t.equal(internals.parseMessageFormat('{level}{if foo}{bar}{end}', log), '{level}') + }) + + t.test('parseMessageFormat removes if / end blocks if nested condition key does not match property key', async t => { + const log = fastCopy(logData) + t.equal(internals.parseMessageFormat('{level}{if data1.msg}{data1.data2}{end}', log), '{level}') + }) + + t.test('parseMessageFormat removes nested if / end statement blocks', async t => { + const log = fastCopy(logData) + t.equal(internals.parseMessageFormat('{if msg}{if data1.data2}{msg}{data1.data2}{end}{end}', log), 'foo{data1.data2}') + }) + t.end() }) From b6894c65367a19e0063b17a719ab4f40c75c6206 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20K=C3=B6ster?= <40566351+timlohse1104@users.noreply.github.com> Date: Mon, 24 Jul 2023 11:57:09 +0200 Subject: [PATCH 4/8] Update docs of messageFormat with more fitting example Co-authored-by: Matteo Collina --- Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index 528c0738..62b23002 100644 --- a/Readme.md +++ b/Readme.md @@ -347,7 +347,7 @@ In addition to this, if / end statement blocks can also be specified. Else state ```js { - messageFormat: '{levelLabel} - {if pid}{pid}{end} - url:{req.url}' + messageFormat: '{levelLabel} - {if pid}{pid} - {end}url:{req.url}' } ``` From 8f224375215cac8b88dc390a5d18a3e833394f2e Mon Sep 17 00:00:00 2001 From: timlohse1104 Date: Mon, 24 Jul 2023 17:01:00 +0200 Subject: [PATCH 5/8] Renamed parseMessageFormat into interpretConditionals, add further interal unit test scenario, refactored interpretConditionals logic to be more readable and renamed internal unit tests to be more fitting --- lib/utils.js | 24 ++++++++-------- test/lib/utils.internals.test.js | 47 ++++++++++++++++++-------------- 2 files changed, 39 insertions(+), 32 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index d585516b..3a2204ad 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -43,7 +43,7 @@ module.exports.internals = { splitPropertyKey, createDate, isValidDate, - parseMessageFormat + interpretConditionals } /** @@ -264,7 +264,7 @@ function prettifyLevel ({ log, colorizer = defaultColorizer, levelKey = LEVEL_KE */ function prettifyMessage ({ log, messageFormat, messageKey = MESSAGE_KEY, colorizer = defaultColorizer, levelLabel = LEVEL_LABEL, levelKey = LEVEL_KEY, customLevels, useOnlyCustomProps }) { if (messageFormat && typeof messageFormat === 'string') { - const parsedMessageFormat = parseMessageFormat(messageFormat, log) + const parsedMessageFormat = interpretConditionals(messageFormat, log) const message = String(parsedMessageFormat).replace( /{([^{}]+)}/g, @@ -784,15 +784,8 @@ function handleCustomlevelNamesOpts (cLevels) { * * @returns {string} The parsed messageFormat. */ -function parseMessageFormat (messageFormat, log) { - messageFormat = messageFormat.replace(/{if (.*?)}(.*?){end}/g, function (_, key, value) { - const propertyValue = getPropertyValue(log, key) - if (propertyValue && value.includes(key)) { - return value.replace(new RegExp('{' + key + '}', 'g'), propertyValue) - } else { - return '' - } - }) +function interpretConditionals (messageFormat, log) { + messageFormat = messageFormat.replace(/{if (.*?)}(.*?){end}/g, replacer) // Remove unescaped if blocks messageFormat = messageFormat.replace(/{if (.*?)}/g, '') @@ -800,4 +793,13 @@ function parseMessageFormat (messageFormat, log) { messageFormat = messageFormat.replace(/{end}/g, '') return messageFormat.replace(/\s+/g, ' ').trim() + + function replacer (_, key, value) { + const propertyValue = getPropertyValue(log, key) + if (propertyValue && value.includes(key)) { + return value.replace(new RegExp('{' + key + '}', 'g'), propertyValue) + } else { + return '' + } + } } diff --git a/test/lib/utils.internals.test.js b/test/lib/utils.internals.test.js index ed3161fc..43919acb 100644 --- a/test/lib/utils.internals.test.js +++ b/test/lib/utils.internals.test.js @@ -223,7 +223,7 @@ tap.test('#getPropertyValue', t => { t.end() }) -tap.test('#parseMessageFormat', t => { +tap.test('#interpretConditionals', t => { const logData = { level: 30, data1: { @@ -232,54 +232,59 @@ tap.test('#parseMessageFormat', t => { msg: 'foo' } - t.test('parseMessageFormat translates if / else statement to found property value', async t => { + t.test('interpretConditionals translates if / else statement to found property value', async t => { const log = fastCopy(logData) - t.equal(internals.parseMessageFormat('{level} - {if data1.data2}{data1.data2}{end}', log), '{level} - bar') + t.equal(internals.interpretConditionals('{level} - {if data1.data2}{data1.data2}{end}', log), '{level} - bar') }) - t.test('parseMessageFormat translates if / else statement to found property value and leave unmatched property key untouched', async t => { + t.test('interpretConditionals translates if / else statement to found property value and leave unmatched property key untouched', async t => { const log = fastCopy(logData) - t.equal(internals.parseMessageFormat('{level} - {if data1.data2}{data1.data2} ({msg}){end}', log), '{level} - bar ({msg})') + t.equal(internals.interpretConditionals('{level} - {if data1.data2}{data1.data2} ({msg}){end}', log), '{level} - bar ({msg})') }) - t.test('parseMessageFormat removes unescaped if statements', async t => { + t.test('interpretConditionals removes non-terminated if statements', async t => { const log = fastCopy(logData) - t.equal(internals.parseMessageFormat('{level} - {if data1.data2}{data1.data2}', log), '{level} - {data1.data2}') + t.equal(internals.interpretConditionals('{level} - {if data1.data2}{data1.data2}', log), '{level} - {data1.data2}') }) - t.test('parseMessageFormat removes unescaped end statements', async t => { + t.test('interpretConditionals removes floating end statements', async t => { const log = fastCopy(logData) - t.equal(internals.parseMessageFormat('{level} - {data1.data2}{end}', log), '{level} - {data1.data2}') + t.equal(internals.interpretConditionals('{level} - {data1.data2}{end}', log), '{level} - {data1.data2}') }) - t.test('parseMessageFormat removes if / end blocks if existent condition key does not match existent property key', async t => { + t.test('interpretConditionals removes floating end statements within translated if / end statements', async t => { const log = fastCopy(logData) - t.equal(internals.parseMessageFormat('{level}{if msg}{data1.data2}{end}', log), '{level}') + t.equal(internals.interpretConditionals('{level} - {if msg}({msg}){end}{end}', log), '{level} - (foo)') }) - t.test('parseMessageFormat removes if / end blocks if non-existent condition key does not match existent property key', async t => { + t.test('interpretConditionals removes if / end blocks if existent condition key does not match existent property key', async t => { const log = fastCopy(logData) - t.equal(internals.parseMessageFormat('{level}{if foo}{msg}{end}', log), '{level}') + t.equal(internals.interpretConditionals('{level}{if msg}{data1.data2}{end}', log), '{level}') }) - t.test('parseMessageFormat removes if / end blocks if existent condition key does not match non-existent property key', async t => { + t.test('interpretConditionals removes if / end blocks if non-existent condition key does not match existent property key', async t => { const log = fastCopy(logData) - t.equal(internals.parseMessageFormat('{level}{if msg}{foo}{end}', log), '{level}') + t.equal(internals.interpretConditionals('{level}{if foo}{msg}{end}', log), '{level}') }) - t.test('parseMessageFormat removes if / end blocks if non-existent condition key does not match non-existent property key', async t => { + t.test('interpretConditionals removes if / end blocks if existent condition key does not match non-existent property key', async t => { const log = fastCopy(logData) - t.equal(internals.parseMessageFormat('{level}{if foo}{bar}{end}', log), '{level}') + t.equal(internals.interpretConditionals('{level}{if msg}{foo}{end}', log), '{level}') }) - t.test('parseMessageFormat removes if / end blocks if nested condition key does not match property key', async t => { + t.test('interpretConditionals removes if / end blocks if non-existent condition key does not match non-existent property key', async t => { const log = fastCopy(logData) - t.equal(internals.parseMessageFormat('{level}{if data1.msg}{data1.data2}{end}', log), '{level}') + t.equal(internals.interpretConditionals('{level}{if foo}{bar}{end}', log), '{level}') }) - t.test('parseMessageFormat removes nested if / end statement blocks', async t => { + t.test('interpretConditionals removes if / end blocks if nested condition key does not match property key', async t => { const log = fastCopy(logData) - t.equal(internals.parseMessageFormat('{if msg}{if data1.data2}{msg}{data1.data2}{end}{end}', log), 'foo{data1.data2}') + t.equal(internals.interpretConditionals('{level}{if data1.msg}{data1.data2}{end}', log), '{level}') + }) + + t.test('interpretConditionals removes nested if / end statement blocks', async t => { + const log = fastCopy(logData) + t.equal(internals.interpretConditionals('{if msg}{if data1.data2}{msg}{data1.data2}{end}{end}', log), 'foo{data1.data2}') }) t.end() From 44ac4c342dff2793f28fa0ff7b3f7f55a10a6ca8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20K=C3=B6ster?= <40566351+timlohse1104@users.noreply.github.com> Date: Mon, 24 Jul 2023 23:08:08 +0200 Subject: [PATCH 6/8] Renamed comments to be more fitting Co-authored-by: James Sumners <321201+jsumners@users.noreply.github.com> --- lib/utils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index 3a2204ad..533578f4 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -787,9 +787,9 @@ function handleCustomlevelNamesOpts (cLevels) { function interpretConditionals (messageFormat, log) { messageFormat = messageFormat.replace(/{if (.*?)}(.*?){end}/g, replacer) - // Remove unescaped if blocks + // Remove non-terminated if blocks messageFormat = messageFormat.replace(/{if (.*?)}/g, '') - // Remove unescaped end blocks + // Remove floating end blocks messageFormat = messageFormat.replace(/{end}/g, '') return messageFormat.replace(/\s+/g, ' ').trim() From 5e4f0f49378e31490c97bd43517567fb2e6985b5 Mon Sep 17 00:00:00 2001 From: timlohse1104 Date: Tue, 25 Jul 2023 16:02:19 +0200 Subject: [PATCH 7/8] Add hideMetadata option, add unit tests and updated docs --- Readme.md | 2 ++ bin.js | 3 ++- index.js | 58 +++++++++++++++++++++++++--------------------- test/basic.test.js | 20 ++++++++++++++++ test/cli.test.js | 13 +++++++++++ 5 files changed, 68 insertions(+), 28 deletions(-) diff --git a/Readme.md b/Readme.md index 62b23002..aae90d91 100644 --- a/Readme.md +++ b/Readme.md @@ -100,6 +100,7 @@ node app.js | pino-pretty - `--hideObject` (`-H`): Hide objects from output (but not error object) - `--singleLine` (`-S`): Print each log message on a single line (errors will still be multi-line) - `--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 +- `--hideMetadata` (`-M`): Hide Metadata (e.g. `timestamp` and `level`) in the beginning of each log line ## Programmatic Integration @@ -258,6 +259,7 @@ The options accepted have keys corresponding to the options described in [CLI Ar levelLabel: 'levelLabel', // --levelLabel minimumLevel: 'info', // --minimumLevel useOnlyCustomProps: true, // --useOnlyCustomProps + hideMetadata: true, // --hideMetadata // The file or file descriptor (1 is stdout) to write to destination: 1, diff --git a/bin.js b/bin.js index 3e18d434..051bfe09 100644 --- a/bin.js +++ b/bin.js @@ -58,7 +58,8 @@ let opts = minimist(process.argv, { ignore: 'i', include: 'I', hideObject: 'H', - singleLine: 'S' + singleLine: 'S', + hideMetadata: 'M' }, default: { messageKey: DEFAULT_VALUE, diff --git a/index.js b/index.js index 41eb7602..84973fc3 100644 --- a/index.js +++ b/index.js @@ -49,7 +49,8 @@ const defaultOptions = { hideObject: false, ignore: 'hostname', include: undefined, - singleLine: false + singleLine: false, + hideMetadata: false } function prettyFactory (options) { @@ -127,39 +128,42 @@ function prettyFactory (options) { log = filterLog({ log, ignoreKeys, includeKeys }) } - const prettifiedLevel = prettifyLevel({ log, colorizer, levelKey, prettifier: customPrettifiers.level, ...customProps }) - const prettifiedMetadata = prettifyMetadata({ log, prettifiers: customPrettifiers }) - const prettifiedTime = prettifyTime({ log, translateFormat: opts.translateTime, timestampKey, prettifier: customPrettifiers.time }) - let line = '' - if (opts.levelFirst && prettifiedLevel) { - line = `${prettifiedLevel}` - } - if (prettifiedTime && line === '') { - line = `${prettifiedTime}` - } else if (prettifiedTime) { - line = `${line} ${prettifiedTime}` - } + if (!opts.hideMetadata) { + const prettifiedLevel = prettifyLevel({ log, colorizer, levelKey, prettifier: customPrettifiers.level, ...customProps }) + const prettifiedMetadata = prettifyMetadata({ log, prettifiers: customPrettifiers }) + const prettifiedTime = prettifyTime({ log, translateFormat: opts.translateTime, timestampKey, prettifier: customPrettifiers.time }) - if (!opts.levelFirst && prettifiedLevel) { - if (line.length > 0) { - line = `${line} ${prettifiedLevel}` - } else { - line = prettifiedLevel + if (opts.levelFirst && prettifiedLevel) { + line = `${prettifiedLevel}` } - } - if (prettifiedMetadata) { - if (line.length > 0) { - line = `${line} ${prettifiedMetadata}:` - } else { - line = prettifiedMetadata + if (prettifiedTime && line === '') { + line = `${prettifiedTime}` + } else if (prettifiedTime) { + line = `${line} ${prettifiedTime}` + } + + if (!opts.levelFirst && prettifiedLevel) { + if (line.length > 0) { + line = `${line} ${prettifiedLevel}` + } else { + line = prettifiedLevel + } } - } - if (line.endsWith(':') === false && line !== '') { - line += ':' + if (prettifiedMetadata) { + if (line.length > 0) { + line = `${line} ${prettifiedMetadata}:` + } else { + line = prettifiedMetadata + } + } + + if (line.endsWith(':') === false && line !== '') { + line += ':' + } } if (prettifiedMessage !== undefined) { diff --git a/test/basic.test.js b/test/basic.test.js index 9986811c..e303cb18 100644 --- a/test/basic.test.js +++ b/test/basic.test.js @@ -118,6 +118,26 @@ test('basic prettifier tests', (t) => { log.info('foo') }) + t.test('can hide date and level', (t) => { + t.plan(1) + const destination = new Writable({ + write (formatted, enc, cb) { + t.equal( + formatted.toString(), + 'foo\n' + ) + cb() + } + }) + const pretty = pinoPretty({ + destination, + hideMetadata: true, + colorize: false + }) + const log = pino({}, pretty) + log.info('foo') + }) + t.test('can print message key value when its a string', (t) => { t.plan(1) const pretty = prettyFactory() diff --git a/test/cli.test.js b/test/cli.test.js index 9b24a078..f1d264af 100644 --- a/test/cli.test.js +++ b/test/cli.test.js @@ -37,6 +37,19 @@ test('cli', (t) => { }) }) + ;['--hideMetadata', '-M'].forEach((optionName) => { + t.test(`hides epoch, level and metadata via ${optionName}`, (t) => { + t.plan(1) + const child = spawn(process.argv[0], [bin, optionName], { env }) + child.on('error', t.threw) + child.stdout.on('data', (data) => { + t.equal(data.toString(), 'hello world\n') + }) + child.stdin.write(logLine) + t.teardown(() => child.kill()) + }) + }) + ;['--translateTime', '-t'].forEach((optionName) => { t.test(`translates time to default format via ${optionName}`, (t) => { t.plan(1) From 8a4066f0fcb5084731d43f416e794dc1400a7f88 Mon Sep 17 00:00:00 2001 From: timlohse1104 Date: Tue, 1 Aug 2023 00:01:13 +0200 Subject: [PATCH 8/8] Refactor nested condition in seperate function --- index.js | 74 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/index.js b/index.js index 84973fc3..566b04a2 100644 --- a/index.js +++ b/index.js @@ -131,39 +131,7 @@ function prettyFactory (options) { let line = '' if (!opts.hideMetadata) { - const prettifiedLevel = prettifyLevel({ log, colorizer, levelKey, prettifier: customPrettifiers.level, ...customProps }) - const prettifiedMetadata = prettifyMetadata({ log, prettifiers: customPrettifiers }) - const prettifiedTime = prettifyTime({ log, translateFormat: opts.translateTime, timestampKey, prettifier: customPrettifiers.time }) - - if (opts.levelFirst && prettifiedLevel) { - line = `${prettifiedLevel}` - } - - if (prettifiedTime && line === '') { - line = `${prettifiedTime}` - } else if (prettifiedTime) { - line = `${line} ${prettifiedTime}` - } - - if (!opts.levelFirst && prettifiedLevel) { - if (line.length > 0) { - line = `${line} ${prettifiedLevel}` - } else { - line = prettifiedLevel - } - } - - if (prettifiedMetadata) { - if (line.length > 0) { - line = `${line} ${prettifiedMetadata}:` - } else { - line = prettifiedMetadata - } - } - - if (line.endsWith(':') === false && line !== '') { - line += ':' - } + line += createLineMetadata() } if (prettifiedMessage !== undefined) { @@ -210,6 +178,46 @@ function prettyFactory (options) { } return line + + function createLineMetadata () { + let lineMetadata = '' + + const prettifiedLevel = prettifyLevel({ log, colorizer, levelKey, prettifier: customPrettifiers.level, ...customProps }) + const prettifiedMetadata = prettifyMetadata({ log, prettifiers: customPrettifiers }) + const prettifiedTime = prettifyTime({ log, translateFormat: opts.translateTime, timestampKey, prettifier: customPrettifiers.time }) + + if (opts.levelFirst && prettifiedLevel) { + lineMetadata = `${prettifiedLevel}` + } + + if (prettifiedTime && lineMetadata === '') { + lineMetadata = `${prettifiedTime}` + } else if (prettifiedTime) { + lineMetadata = `${lineMetadata} ${prettifiedTime}` + } + + if (!opts.levelFirst && prettifiedLevel) { + if (lineMetadata.length > 0) { + lineMetadata = `${lineMetadata} ${prettifiedLevel}` + } else { + lineMetadata = prettifiedLevel + } + } + + if (prettifiedMetadata) { + if (lineMetadata.length > 0) { + lineMetadata = `${lineMetadata} ${prettifiedMetadata}:` + } else { + lineMetadata = prettifiedMetadata + } + } + + if (lineMetadata.endsWith(':') === false && lineMetadata !== '') { + lineMetadata += ':' + } + + return lineMetadata + } } }