From 6da74ee624a920a68f14aca86a92187abf4de158 Mon Sep 17 00:00:00 2001 From: Frederick Zhang Date: Tue, 14 Jan 2025 14:20:02 +1100 Subject: [PATCH 1/3] fix: loosen up customPrettifiers typing constraints The fourth parameter of the 'level' custom prettifier -- extras -- used to have a separate type as pino-pretty offered two additional attributes to this particular prettifier. However since it used a Record type, TypeScript required all values to be compatible with each other. So when users tried to use the extras parameter for 'level', it caused error TS2322: Type '{ level: (_level: string | object, _levelKey: string, _log: object, { labelColorized }: PrettifierExtras) => string; }' is not assignable to type 'Record> & { level?: Prettifier | undefined; }'. Type '{ level: (_level: string | object, _levelKey: string, _log: object, { labelColorized }: PrettifierExtras) => string; }' is not assignable to type 'Record>'. Property 'level' is incompatible with index signature. Type '(_level: string | object, _levelKey: string, _log: object, { labelColorized }: PrettifierExtras) => string' is not assignable to type 'Prettifier'. Types of parameters '__3' and 'extras' are incompatible. Type 'PrettifierExtras' is not assignable to type 'PrettifierExtras'. Type '{ colors: Colorette; }' is missing the following properties from type 'LevelPrettifierExtras': label, labelColorized 115 customPrettifiers: { ~~~~~~~~~~~~~~~~~ This patch remedies this issue by merging LevelPrettifierExtras into PrettifierExtras. These types are not exported directly, therefore users, including those who leverage TypeScript utility types to extract these types, should be able to upgrade directly. Fixes #550 --- index.d.ts | 7 +++---- test/types/pino-pretty.test-d.ts | 5 ++++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/index.d.ts b/index.d.ts index 2c4c5c8b..8aa35ca0 100644 --- a/index.d.ts +++ b/index.d.ts @@ -185,7 +185,7 @@ declare namespace PinoPretty { */ customPrettifiers?: Record & { - level?: Prettifier + level?: Prettifier }; /** * Change the level names and values to an user custom preset. @@ -218,9 +218,8 @@ declare namespace PinoPretty { function build(options: PrettyOptions): PrettyStream; - type Prettifier = (inputData: string | object, key: string, log: object, extras: PrettifierExtras) => string; - type PrettifierExtras = {colors: Colorette.Colorette} & T; - type LevelPrettifierExtras = {label: string, labelColorized: string} + type Prettifier = (inputData: string | object, key: string, log: object, extras: PrettifierExtras) => string; + type PrettifierExtras = {colors: Colorette.Colorette, label: string, labelColorized: string}; type MessageFormatFunc = (log: LogDescriptor, messageKey: string, levelLabel: string, extras: PrettifierExtras) => string; type PrettyStream = Transform & OnUnknown; type ColorizerFactory = typeof colorizerFactory; diff --git a/test/types/pino-pretty.test-d.ts b/test/types/pino-pretty.test-d.ts index 9f2acd71..8649edc3 100644 --- a/test/types/pino-pretty.test-d.ts +++ b/test/types/pino-pretty.test-d.ts @@ -33,8 +33,11 @@ const options: PinoPretty.PrettyOptions = { key: (value) => { return value.toString().toUpperCase(); }, - level: (level, label, colorized) => { + level: (level, levelKey, log, { label, labelColorized, colors }) => { return level.toString(); + }, + foo: (value, key, log, { colors }) => { + return value.toString(); } }, customLevels: 'verbose:5', From 38cdaf27e1e2a1f33203aa249637e6e6d3086b78 Mon Sep 17 00:00:00 2001 From: Frederick Zhang Date: Tue, 14 Jan 2025 15:25:39 +1100 Subject: [PATCH 2/3] feat!: change customPrettifiers to a Map In [1], LevelPrettifierExtras and PrettifierExtras were merged to solve a typing issue [2]. This was suboptimal as the additional extra attributes that only the 'level' prettifier had were exposed to other prettifiers too. To allow the additional attributes while keeping 'level' prettifier's type separate, this patch uses an intersection Map type CustomPrettifiers instead. This is a breaking change. [1] https://github.com/pinojs/pino-pretty/pull/551 [2] https://github.com/pinojs/pino-pretty/issues/550 --- Readme.md | 32 ++++---- benchmark.js | 12 +-- index.d.ts | 23 +++--- index.js | 4 +- lib/utils/index.js | 8 +- lib/utils/parse-factory-options.js | 2 +- lib/utils/prettify-error-log.test.js | 2 +- lib/utils/prettify-level.js | 2 +- lib/utils/prettify-level.test.js | 4 +- lib/utils/prettify-metadata.js | 16 ++-- lib/utils/prettify-metadata.test.js | 56 +++++++------- lib/utils/prettify-object.js | 8 +- lib/utils/prettify-object.test.js | 32 ++++---- lib/utils/prettify-time.js | 2 +- lib/utils/prettify-time.test.js | 10 +-- test/basic.test.js | 108 ++++++++++++++------------- test/error-objects.test.js | 6 +- test/types/pino-pretty.test-d.ts | 26 ++++--- 18 files changed, 180 insertions(+), 173 deletions(-) diff --git a/Readme.md b/Readme.md index 2f35e7f2..30d260c9 100644 --- a/Readme.md +++ b/Readme.md @@ -282,7 +282,7 @@ The options accepted have keys corresponding to the options described in [CLI Ar mkdir: true, // create the target destination - customPrettifiers: {} + customPrettifiers: new Map() } ``` @@ -293,16 +293,16 @@ The defaults for `sync`, `append`, `mkdir` inherit from [`SonicBoom(opts)`](https://github.com/pinojs/sonic-boom#API). `customPrettifiers` option provides the ability to add a custom prettify function -for specific log properties. `customPrettifiers` is an object, where keys are -log properties that will be prettified and value is the prettify function itself. +for specific log properties. `customPrettifiers` is a `Map`, where keys are log +properties that will be prettified and value is the prettify function itself. For example, if a log line contains a `query` property, you can specify a prettifier for it: ```js { - customPrettifiers: { - query: prettifyQuery - } + customPrettifiers: new Map([ + ['query', prettifyQuery] + ]) } //... const prettifyQuery = value => { @@ -330,27 +330,27 @@ An example usage of `customPrettifiers` using all parameters from the function s ```js { - customPrettifiers: { + customPrettifiers: new Map([ // The argument for this function will be the same // string that's at the start of the log-line by default: - time: timestamp => `🕰 ${timestamp}`, + ['time', timestamp => `🕰 ${timestamp}`], // The argument for the level-prettifier may vary depending // on if the levelKey option is used or not. // By default this will be the same numerics as the Pino default: - level: logLevel => `LEVEL: ${logLevel}`, + ['level', logLevel => `LEVEL: ${logLevel}`], // level provides additional data in `extras`: // * label => derived level label string // * labelColorized => derived level label string with colorette colors applied based on customColors and whether colors are supported - level: (logLevel, key, log, { label, labelColorized, colors }) => `LEVEL: ${logLevel} LABEL: ${levelLabel} COLORIZED LABEL: ${labelColorized}`, + ['level', (logLevel, key, log, { label, labelColorized, colors }) => `LEVEL: ${logLevel} LABEL: ${levelLabel} COLORIZED LABEL: ${labelColorized}`], // other prettifiers can be used for the other keys if needed, for example - hostname: hostname => `MY HOST: ${hostname}`, - pid: pid => pid, - name: (name, key, log, { colors }) => `${colors.blue(name)}`, - caller: (caller, key, log, { colors }) => `${colors.greenBright(caller)}`, - myCustomLogProp: (value, key, log, { colors }) => `My Prop -> ${colors.bold(value)} <--` - } + ['hostname', hostname => `MY HOST: ${hostname}`], + ['pid', pid => pid], + ['name', (name, key, log, { colors }) => `${colors.blue(name)}`], + ['caller', (caller, key, log, { colors }) => `${colors.greenBright(caller)}`], + ['myCustomLogProp', (value, key, log, { colors }) => `My Prop -> ${colors.bold(value)} <--`] + ]) } ``` diff --git a/benchmark.js b/benchmark.js index aaf7412e..43825bc0 100644 --- a/benchmark.js +++ b/benchmark.js @@ -51,14 +51,14 @@ const run = bench([ function customPrettifiers (cb) { const pretty = prettyFactory({ - customPrettifiers: { - time (tstamp) { + customPrettifiers: new Map([ + ['time', (tstamp) => { return tstamp - }, - pid () { + }], + ['pid', () => { return '' - } - } + }] + ]) }) const input = `{"time":${tstampMillis},"pid":1,"hostname":"foo","msg":"benchmark","foo":"foo","bar":{"bar":"bar"}}\n` for (var i = 0; i < max; i += 1) { diff --git a/index.d.ts b/index.d.ts index 8aa35ca0..c488e396 100644 --- a/index.d.ts +++ b/index.d.ts @@ -165,17 +165,17 @@ declare namespace PinoPretty { mkdir?: boolean; /** * Provides the ability to add a custom prettify function for specific log properties. - * `customPrettifiers` is an object, where keys are log properties that will be prettified + * `customPrettifiers` is a Map, where keys are log properties that will be prettified * and value is the prettify function itself. * For example, if a log line contains a query property, you can specify a prettifier for it: - * @default {} + * @default new Map() * * @example * ```typescript * { - * customPrettifiers: { - * query: prettifyQuery - * } + * customPrettifiers: new Map([ + * ['query', prettifyQuery] + * ]) * } * //... * const prettifyQuery = value => { @@ -183,10 +183,7 @@ declare namespace PinoPretty { * } * ``` */ - customPrettifiers?: Record & - { - level?: Prettifier - }; + customPrettifiers?: CustomPrettifiers; /** * Change the level names and values to an user custom preset. * @@ -218,8 +215,10 @@ declare namespace PinoPretty { function build(options: PrettyOptions): PrettyStream; - type Prettifier = (inputData: string | object, key: string, log: object, extras: PrettifierExtras) => string; - type PrettifierExtras = {colors: Colorette.Colorette, label: string, labelColorized: string}; + type CustomPrettifiers = Map<'level', Prettifier> & Map + type Prettifier = (inputData: string | object, key: string, log: object, extras: PrettifierExtras) => string; + type PrettifierExtras = {colors: Colorette.Colorette} & T; + type LevelPrettifierExtras = {label: string, labelColorized: string} type MessageFormatFunc = (log: LogDescriptor, messageKey: string, levelLabel: string, extras: PrettifierExtras) => string; type PrettyStream = Transform & OnUnknown; type ColorizerFactory = typeof colorizerFactory; @@ -227,7 +226,7 @@ declare namespace PinoPretty { type Build = typeof build; type isColorSupported = typeof Colorette.isColorSupported; - export { build, PinoPretty, PrettyOptions, PrettyStream, colorizerFactory, prettyFactory, isColorSupported }; + export { build, PinoPretty, PrettyOptions, CustomPrettifiers, PrettyStream, colorizerFactory, prettyFactory, isColorSupported }; } export = PinoPretty; diff --git a/index.js b/index.js index 528b2b0c..3bfeecc2 100644 --- a/index.js +++ b/index.js @@ -30,7 +30,7 @@ const pretty = require('./lib/pretty') * to use for specific level labels, e.g. `err:red,info:blue`. * @property {string|null} [customLevels=null] A comma separated list of user * defined level names and numbers, e.g. `err:99,info:1`. - * @property {CustomPrettifiers} [customPrettifiers={}] A set of prettifier + * @property {CustomPrettifiers} [customPrettifiers=Map] A set of prettifier * functions to apply to keys defined in this object. * @property {K_ERROR_LIKE_KEYS} [errorLikeObjectKeys] A list of string property * names to consider as error objects. @@ -83,7 +83,7 @@ const defaultOptions = { crlf: false, customColors: null, customLevels: null, - customPrettifiers: {}, + customPrettifiers: new Map(), errorLikeObjectKeys: ERROR_LIKE_KEYS, errorProps: '', hideObject: false, diff --git a/lib/utils/index.js b/lib/utils/index.js index e083d737..e596948b 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -34,13 +34,13 @@ module.exports = { // doc blocks, so we are picking this file as the answer. /** - * A hash of log property names mapped to prettifier functions. When the + * A map of log property names mapped to prettifier functions. When the * incoming log data is being processed for prettification, any key on the log - * that matches a key in a custom prettifiers hash will be prettified using + * that matches a key in a custom prettifiers map will be prettified using * that matching custom prettifier. The value passed to the custom prettifier * will the value associated with the corresponding log key. * - * The hash may contain any arbitrary keys for arbitrary log properties, but it + * The map may contain any arbitrary keys for arbitrary log properties, but it * may also contain a set of predefined key names that map to well-known log * properties. These keys are: * @@ -52,7 +52,7 @@ module.exports = { * + `name` * + `caller` * - * @typedef {Object.} CustomPrettifiers + * @typedef {Map} CustomPrettifiers */ /** diff --git a/lib/utils/parse-factory-options.js b/lib/utils/parse-factory-options.js index 3b051f03..8fc327bf 100644 --- a/lib/utils/parse-factory-options.js +++ b/lib/utils/parse-factory-options.js @@ -25,7 +25,7 @@ const handleLevelLabelData = require('./get-level-label-data') * e.g. `{ 30: "info" }`. * @property {object} customLevels A hash of level names to level numbers, * e.g. `{ info: 30 }`. - * @property {CustomPrettifiers} customPrettifiers A hash of custom prettifier + * @property {CustomPrettifiers} customPrettifiers A map of custom prettifier * functions. * @property {object} customProperties Comprised of `customLevels` and * `customLevelNames` if such options are provided. diff --git a/lib/utils/prettify-error-log.test.js b/lib/utils/prettify-error-log.test.js index 04e10f82..d1048002 100644 --- a/lib/utils/prettify-error-log.test.js +++ b/lib/utils/prettify-error-log.test.js @@ -10,7 +10,7 @@ const { const context = { EOL: '\n', IDENT: ' ', - customPrettifiers: {}, + customPrettifiers: new Map(), errorLikeObjectKeys: ERROR_LIKE_KEYS, errorProps: [], messageKey: MESSAGE_KEY diff --git a/lib/utils/prettify-level.js b/lib/utils/prettify-level.js index 2e4dce84..e401a38b 100644 --- a/lib/utils/prettify-level.js +++ b/lib/utils/prettify-level.js @@ -29,7 +29,7 @@ function prettifyLevel ({ log, context }) { levelKey, getLevelLabelData } = context - const prettifier = context.customPrettifiers?.level + const prettifier = context.customPrettifiers?.get('level') const output = getPropertyValue(log, levelKey) if (output === undefined) return undefined const labelColorized = colorizer(output, { customLevels, customLevelNames }) diff --git a/lib/utils/prettify-level.test.js b/lib/utils/prettify-level.test.js index e6e5a3c1..1ce01089 100644 --- a/lib/utils/prettify-level.test.js +++ b/lib/utils/prettify-level.test.js @@ -63,7 +63,9 @@ tap.test('passes output through provided prettifier', async t => { log, context: { ...context, - customPrettifiers: { level () { return 'modified' } } + customPrettifiers: new Map([ + ['level', () => { return 'modified' }] + ]) } }) t.equal(colorized, 'modified') diff --git a/lib/utils/prettify-metadata.js b/lib/utils/prettify-metadata.js index 72483b14..0aab43fa 100644 --- a/lib/utils/prettify-metadata.js +++ b/lib/utils/prettify-metadata.js @@ -28,14 +28,14 @@ function prettifyMetadata ({ log, context }) { line += '(' if (log.name) { - line += prettifiers.name - ? prettifiers.name(log.name, 'name', log, { colors: colorizer.colors }) + line += prettifiers.get('name') + ? prettifiers.get('name')(log.name, 'name', log, { colors: colorizer.colors }) : log.name } if (log.pid) { - const prettyPid = prettifiers.pid - ? prettifiers.pid(log.pid, 'pid', log, { colors: colorizer.colors }) + const prettyPid = prettifiers.get('pid') + ? prettifiers.get('pid')(log.pid, 'pid', log, { colors: colorizer.colors }) : log.pid if (log.name && log.pid) { line += '/' + prettyPid @@ -47,8 +47,8 @@ function prettifyMetadata ({ log, context }) { if (log.hostname) { // If `pid` and `name` were in the ignore keys list then we don't need // the leading space. - const prettyHostname = prettifiers.hostname - ? prettifiers.hostname(log.hostname, 'hostname', log, { colors: colorizer.colors }) + const prettyHostname = prettifiers.get('hostname') + ? prettifiers.get('hostname')(log.hostname, 'hostname', log, { colors: colorizer.colors }) : log.hostname line += `${line === '(' ? 'on' : ' on'} ${prettyHostname}` @@ -58,8 +58,8 @@ function prettifyMetadata ({ log, context }) { } if (log.caller) { - const prettyCaller = prettifiers.caller - ? prettifiers.caller(log.caller, 'caller', log, { colors: colorizer.colors }) + const prettyCaller = prettifiers.get('caller') + ? prettifiers.get('caller')(log.caller, 'caller', log, { colors: colorizer.colors }) : log.caller line += `${line === '' ? '' : ' '}<${prettyCaller}>` diff --git a/lib/utils/prettify-metadata.test.js b/lib/utils/prettify-metadata.test.js index cf2f892c..a95a8c9c 100644 --- a/lib/utils/prettify-metadata.test.js +++ b/lib/utils/prettify-metadata.test.js @@ -4,7 +4,7 @@ const tap = require('tap') const prettifyMetadata = require('./prettify-metadata') const getColorizer = require('../colors') const context = { - customPrettifiers: {}, + customPrettifiers: new Map(), colorizer: { colors: {} } @@ -91,20 +91,19 @@ tap.test('works with all four present', async t => { }) tap.test('uses prettifiers from passed prettifiers object', async t => { - const prettifiers = { - name (input) { - return input.toUpperCase() - }, - pid (input) { - return input + '__' - }, - hostname (input) { - return input.toUpperCase() - }, - caller (input) { - return input.toUpperCase() - } - } + const prettifiers = new Map() + prettifiers.set('name', (input) => { + return input.toUpperCase() + }) + prettifiers.set('pid', (input) => { + return input + '__' + }) + prettifiers.set('hostname', (input) => { + return input.toUpperCase() + }) + prettifiers.set('caller', (input) => { + return input.toUpperCase() + }) const str = prettifyMetadata({ log: { pid: '1234', hostname: 'bar', caller: 'baz', name: 'joe' }, context: { @@ -116,20 +115,19 @@ tap.test('uses prettifiers from passed prettifiers object', async t => { }) tap.test('uses colorizer from passed context to colorize metadata', async t => { - const prettifiers = { - name (input, _key, _log, { colors }) { - return colors.blue(input) - }, - pid (input, _key, _log, { colors }) { - return colors.red(input) - }, - hostname (input, _key, _log, { colors }) { - return colors.green(input) - }, - caller (input, _key, _log, { colors }) { - return colors.cyan(input) - } - } + const prettifiers = new Map() + prettifiers.set('name', (input, _key, _log, { colors }) => { + return colors.blue(input) + }) + prettifiers.set('pid', (input, _key, _log, { colors }) => { + return colors.red(input) + }) + prettifiers.set('hostname', (input, _key, _log, { colors }) => { + return colors.green(input) + }) + prettifiers.set('caller', (input, _key, _log, { colors }) => { + return colors.cyan(input) + }) const log = { name: 'foo', pid: '1234', hostname: 'bar', caller: 'baz' } const colorizer = getColorizer(true) const context = { diff --git a/lib/utils/prettify-object.js b/lib/utils/prettify-object.js index 5de0f5cb..5f1ecea5 100644 --- a/lib/utils/prettify-object.js +++ b/lib/utils/prettify-object.js @@ -57,8 +57,8 @@ function prettifyObject ({ const { plain, errors } = Object.entries(log).reduce(({ plain, errors }, [k, v]) => { if (keysToIgnore.includes(k) === false) { // Pre-apply custom prettifiers, because all 3 cases below will need this - const pretty = typeof customPrettifiers[k] === 'function' - ? customPrettifiers[k](v, k, log, { colors: colorizer.colors }) + const pretty = typeof customPrettifiers.get(k) === 'function' + ? customPrettifiers.get(k)(v, k, log, { colors: colorizer.colors }) : v if (errorLikeKeys.includes(k)) { errors[k] = pretty @@ -82,7 +82,7 @@ function prettifyObject ({ // Put each object entry on its own line Object.entries(plain).forEach(([keyName, keyValue]) => { // custom prettifiers are already applied above, so we can skip it now - let lines = typeof customPrettifiers[keyName] === 'function' + let lines = typeof customPrettifiers.get(keyName) === 'function' ? keyValue : stringifySafe(keyValue, null, 2) @@ -99,7 +99,7 @@ function prettifyObject ({ // Errors Object.entries(errors).forEach(([keyName, keyValue]) => { // custom prettifiers are already applied above, so we can skip it now - const lines = typeof customPrettifiers[keyName] === 'function' + const lines = typeof customPrettifiers.get(keyName) === 'function' ? keyValue : stringifySafe(keyValue, null, 2) diff --git a/lib/utils/prettify-object.test.js b/lib/utils/prettify-object.test.js index 2dde4a9a..a3ea87b8 100644 --- a/lib/utils/prettify-object.test.js +++ b/lib/utils/prettify-object.test.js @@ -11,7 +11,7 @@ const getColorizer = require('../colors') const context = { EOL: '\n', IDENT: ' ', - customPrettifiers: {}, + customPrettifiers: new Map(), errorLikeObjectKeys: ERROR_LIKE_KEYS, objectColorizer: colors(), singleLine: false, @@ -76,9 +76,9 @@ tap.test('works with error props', async t => { }) tap.test('customPrettifiers gets applied', async t => { - const customPrettifiers = { - foo: v => v.toUpperCase() - } + const customPrettifiers = new Map([ + ['foo', v => v.toUpperCase()] + ]) const str = prettifyObject({ log: { foo: 'foo' }, context: { @@ -90,9 +90,9 @@ tap.test('customPrettifiers gets applied', async t => { }) tap.test('skips lines omitted by customPrettifiers', async t => { - const customPrettifiers = { - foo: () => { return undefined } - } + const customPrettifiers = new Map([ + ['foo', () => { return undefined }] + ]) const str = prettifyObject({ log: { foo: 'foo', bar: 'bar' }, context: { @@ -110,9 +110,9 @@ tap.test('joined lines omits starting eol', async t => { context: { ...context, IDENT: '', - customPrettifiers: { - calls: val => '\n' + val.map(it => ' ' + it).join('\n') - } + customPrettifiers: new Map([ + ['calls', val => '\n' + val.map(it => ' ' + it).join('\n')] + ]) } }) t.equal(str, [ @@ -126,9 +126,9 @@ tap.test('joined lines omits starting eol', async t => { }) tap.test('errors skips prettifiers', async t => { - const customPrettifiers = { - err: () => { return 'is_err' } - } + const customPrettifiers = new Map([ + ['err', () => { return 'is_err' }] + ]) const str = prettifyObject({ log: { err: Error('boom') }, context: { @@ -140,9 +140,9 @@ tap.test('errors skips prettifiers', async t => { }) tap.test('errors skips prettifying if no lines are present', async t => { - const customPrettifiers = { - err: () => { return undefined } - } + const customPrettifiers = new Map([ + ['err', () => { return undefined }] + ]) const str = prettifyObject({ log: { err: Error('boom') }, context: { diff --git a/lib/utils/prettify-time.js b/lib/utils/prettify-time.js index e876b35f..6f4fa95a 100644 --- a/lib/utils/prettify-time.js +++ b/lib/utils/prettify-time.js @@ -26,7 +26,7 @@ function prettifyTime ({ log, context }) { timestampKey, translateTime: translateFormat } = context - const prettifier = context.customPrettifiers?.time + const prettifier = context.customPrettifiers?.get('time') let time = null if (timestampKey in log) { diff --git a/lib/utils/prettify-time.test.js b/lib/utils/prettify-time.test.js index cfde6627..44ec8ebb 100644 --- a/lib/utils/prettify-time.test.js +++ b/lib/utils/prettify-time.test.js @@ -10,7 +10,7 @@ const { const context = { timestampKey: TIMESTAMP_KEY, translateTime: true, - customPrettifiers: {} + customPrettifiers: new Map() } tap.test('returns `undefined` if `time` or `timestamp` not in log', async t => { @@ -216,11 +216,9 @@ tap.test('uses custom prettifier', async t => { log: { time: 0 }, context: { ...context, - customPrettifiers: { - time () { - return 'done' - } - } + customPrettifiers: new Map([ + ['time', () => { return 'done' }] + ]) } }) t.equal(str, 'done') diff --git a/test/basic.test.js b/test/basic.test.js index e2a58304..107ddb09 100644 --- a/test/basic.test.js +++ b/test/basic.test.js @@ -240,9 +240,9 @@ test('basic prettifier tests', (t) => { 30: 'ok', 40: 'not great' } - const customPrettifiers = { - level: (level) => `LEVEL: ${veryCustomLevels[level]}` - } + const customPrettifiers = new Map([ + ['level', (level) => `LEVEL: ${veryCustomLevels[level]}`] + ]) const pretty = prettyFactory({ customPrettifiers }) const log = pino({}, new Writable({ write (chunk, enc, cb) { @@ -259,9 +259,9 @@ test('basic prettifier tests', (t) => { t.test('can use a customPrettifier on different-level-key output', (t) => { t.plan(1) - const customPrettifiers = { - level: (level) => `LEVEL: ${level.toUpperCase()}` - } + const customPrettifiers = new Map([ + ['level', (level) => `LEVEL: ${level.toUpperCase()}`] + ]) const pretty = prettyFactory({ levelKey: 'bar', customPrettifiers }) const log = pino({}, new Writable({ write (chunk, enc, cb) { @@ -278,11 +278,14 @@ test('basic prettifier tests', (t) => { t.test('can use a customPrettifier to get final level label (no color)', (t) => { t.plan(1) - const customPrettifiers = { - level: (level, key, logThis, { label }) => { - return `LEVEL: ${label}` - } - } + const customPrettifiers = new Map([ + [ + 'level', + (level, key, logThis, { label }) => { + return `LEVEL: ${label}` + } + ] + ]) const pretty = prettyFactory({ customPrettifiers, colorize: false, useOnlyCustomProps: false }) const log = pino({}, new Writable({ write (chunk, enc, cb) { @@ -299,11 +302,14 @@ test('basic prettifier tests', (t) => { t.test('can use a customPrettifier to get final level label (colorized)', (t) => { t.plan(1) - const customPrettifiers = { - level: (level, key, logThis, { label, labelColorized }) => { - return `LEVEL: ${labelColorized}` - } - } + const customPrettifiers = new Map([ + [ + 'level', + (level, key, logThis, { label, labelColorized }) => { + return `LEVEL: ${labelColorized}` + } + ] + ]) const pretty = prettyFactory({ customPrettifiers, colorize: true, useOnlyCustomProps: false }) const log = pino({}, new Writable({ write (chunk, enc, cb) { @@ -320,9 +326,9 @@ test('basic prettifier tests', (t) => { t.test('can use a customPrettifier on name output', (t) => { t.plan(1) - const customPrettifiers = { - name: (hostname) => `NAME: ${hostname}` - } + const customPrettifiers = new Map([ + ['name', (hostname) => `NAME: ${hostname}`] + ]) const pretty = prettyFactory({ customPrettifiers }) const log = pino({}, new Writable({ write (chunk, enc, cb) { @@ -340,10 +346,10 @@ test('basic prettifier tests', (t) => { t.test('can use a customPrettifier on hostname and pid output', (t) => { t.plan(1) - const customPrettifiers = { - hostname: (hostname) => `HOSTNAME: ${hostname}`, - pid: (pid) => `PID: ${pid}` - } + const customPrettifiers = new Map([ + ['hostname', (hostname) => `HOSTNAME: ${hostname}`], + ['pid', (pid) => `PID: ${pid}`] + ]) const pretty = prettyFactory({ customPrettifiers, ignore: '' }) const log = pino({}, new Writable({ write (chunk, enc, cb) { @@ -360,9 +366,9 @@ test('basic prettifier tests', (t) => { t.test('can use a customPrettifier on default time output', (t) => { t.plan(1) - const customPrettifiers = { - time: (timestamp) => `TIME: ${timestamp}` - } + const customPrettifiers = new Map([ + ['time', (timestamp) => `TIME: ${timestamp}`] + ]) const pretty = prettyFactory({ customPrettifiers }) const log = pino({}, new Writable({ write (chunk, enc, cb) { @@ -379,9 +385,9 @@ test('basic prettifier tests', (t) => { t.test('can use a customPrettifier on the caller', (t) => { t.plan(1) - const customPrettifiers = { - caller: (caller) => `CALLER: ${caller}` - } + const customPrettifiers = new Map([ + ['caller', (caller) => `CALLER: ${caller}`] + ]) const pretty = prettyFactory({ customPrettifiers }) const log = pino({}, new Writable({ write (chunk, enc, cb) { @@ -398,9 +404,9 @@ test('basic prettifier tests', (t) => { t.test('can use a customPrettifier on translateTime-time output', (t) => { t.plan(1) - const customPrettifiers = { - time: (timestamp) => `TIME: ${timestamp}` - } + const customPrettifiers = new Map([ + ['time', (timestamp) => `TIME: ${timestamp}`] + ]) const pretty = prettyFactory({ customPrettifiers, translateTime: true }) const log = pino({}, new Writable({ write (chunk, enc, cb) { @@ -824,10 +830,10 @@ test('basic prettifier tests', (t) => { t.test('prettifies custom key', (t) => { t.plan(1) const pretty = prettyFactory({ - customPrettifiers: { - foo: val => `${val}_baz\nmultiline`, - cow: val => val.toUpperCase() - } + customPrettifiers: new Map([ + ['foo', val => `${val}_baz\nmultiline`], + ['cow', val => val.toUpperCase()] + ]) }) const arst = pretty('{"msg":"hello world", "foo": "bar", "cow": "moo", "level":30}') t.equal(arst, 'INFO: hello world\n foo: bar_baz\n multiline\n cow: MOO\n') @@ -836,9 +842,9 @@ test('basic prettifier tests', (t) => { t.test('does not add trailing space if prettified value begins with eol', (t) => { t.plan(1) const pretty = prettyFactory({ - customPrettifiers: { - calls: val => '\n' + val.map(it => ' ' + it).join('\n') - } + customPrettifiers: new Map([ + ['calls', val => '\n' + val.map(it => ' ' + it).join('\n')] + ]) }) const arst = pretty('{"msg":"doing work","calls":["step 1","step 2","step 3"],"level":30}') t.equal(arst, 'INFO: doing work\n calls:\n step 1\n step 2\n step 3\n') @@ -847,10 +853,10 @@ test('basic prettifier tests', (t) => { t.test('does not prettify custom key that does not exists', (t) => { t.plan(1) const pretty = prettyFactory({ - customPrettifiers: { - foo: val => `${val}_baz`, - cow: val => val.toUpperCase() - } + customPrettifiers: new Map([ + ['foo', val => `${val}_baz`], + ['cow', val => val.toUpperCase()] + ]) }) const arst = pretty('{"msg":"hello world", "foo": "bar", "level":30}') t.equal(arst, 'INFO: hello world\n foo: bar_baz\n') @@ -1054,10 +1060,10 @@ test('basic prettifier tests', (t) => { const pretty = prettyFactory({ singleLine: true, colorize: false, - customPrettifiers: { - upper: val => val.toUpperCase(), - undef: () => undefined - } + customPrettifiers: new Map([ + ['upper', val => val.toUpperCase()], + ['undef', () => undefined] + ]) }) const log = pino({}, new Writable({ write (chunk, enc, cb) { @@ -1159,10 +1165,10 @@ test('basic prettifier tests', (t) => { mkdir: true, append: false, destination: new SonicBoom({ dest: destination, async: false, mkdir: true, append: true }), - customPrettifiers: { - upper: val => val.toUpperCase(), - undef: () => undefined - } + customPrettifiers: new Map([ + ['upper', val => val.toUpperCase()], + ['undef', () => undefined] + ]) }) const log = pino(pretty) log.info({ msg: 'message', extra: { foo: 'bar', number: 42 }, upper: 'foobar', undef: 'this will not show up' }) diff --git a/test/error-objects.test.js b/test/error-objects.test.js index b9840c6c..653f3a47 100644 --- a/test/error-objects.test.js +++ b/test/error-objects.test.js @@ -151,9 +151,9 @@ test('error like objects tests', (t) => { t.plan(4) const pretty = prettyFactory({ errorLikeObjectKeys: ['err'], - customPrettifiers: { - err: val => `error is ${val.message}` - } + customPrettifiers: new Map([ + ['err', val => `error is ${val.message}`] + ]) }) const err = Error('hello world') diff --git a/test/types/pino-pretty.test-d.ts b/test/types/pino-pretty.test-d.ts index 8649edc3..b724b046 100644 --- a/test/types/pino-pretty.test-d.ts +++ b/test/types/pino-pretty.test-d.ts @@ -4,6 +4,7 @@ import pretty from "../../"; import PinoPretty, { PinoPretty as PinoPrettyNamed, PrettyOptions, + CustomPrettifiers, colorizerFactory, prettyFactory } from "../../"; @@ -13,6 +14,19 @@ import PinoPrettyCjsImport = require("../../"); import PrettyStream = PinoPretty.PrettyStream; const PinoPrettyCjs = require("../../"); +const customPrettifiers: CustomPrettifiers = new Map(); +customPrettifiers.set("key", (value) => { + return value.toString().toUpperCase(); +}); +customPrettifiers.set( + "level", + (level, levelKey, log, { label, labelColorized, colors }) => { + return level.toString(); + } +); +customPrettifiers.set("foo", (value, key, log, { colors }) => { + return value.toString(); +}); const options: PinoPretty.PrettyOptions = { colorize: true, crlf: false, @@ -29,17 +43,7 @@ const options: PinoPretty.PrettyOptions = { minimumLevel: "trace", translateTime: "UTC:h:MM:ss TT Z", singleLine: false, - customPrettifiers: { - key: (value) => { - return value.toString().toUpperCase(); - }, - level: (level, levelKey, log, { label, labelColorized, colors }) => { - return level.toString(); - }, - foo: (value, key, log, { colors }) => { - return value.toString(); - } - }, + customPrettifiers, customLevels: 'verbose:5', customColors: 'default:white,verbose:gray', sync: false, From 250d0d91b362fd0630252cd0105c869a61b8a448 Mon Sep 17 00:00:00 2001 From: Frederick Zhang Date: Fri, 17 Jan 2025 10:42:04 +1100 Subject: [PATCH 3/3] fixup! feat!: change customPrettifiers to a Map --- Readme.md | 41 +++++---- benchmark.js | 12 +-- index.js | 7 +- lib/utils/index.js | 8 +- lib/utils/parse-factory-options.js | 2 +- lib/utils/prettify-error-log.test.js | 2 +- lib/utils/prettify-level.js | 2 +- lib/utils/prettify-level.test.js | 4 +- lib/utils/prettify-metadata.js | 16 ++-- lib/utils/prettify-metadata.test.js | 56 ++++++------ lib/utils/prettify-object.js | 8 +- lib/utils/prettify-object.test.js | 32 +++---- lib/utils/prettify-time.js | 2 +- lib/utils/prettify-time.test.js | 10 ++- test/basic.test.js | 127 +++++++++++++++------------ test/error-objects.test.js | 6 +- 16 files changed, 182 insertions(+), 153 deletions(-) diff --git a/Readme.md b/Readme.md index 30d260c9..d2174235 100644 --- a/Readme.md +++ b/Readme.md @@ -282,7 +282,7 @@ The options accepted have keys corresponding to the options described in [CLI Ar mkdir: true, // create the target destination - customPrettifiers: new Map() + customPrettifiers: {} } ``` @@ -293,16 +293,16 @@ The defaults for `sync`, `append`, `mkdir` inherit from [`SonicBoom(opts)`](https://github.com/pinojs/sonic-boom#API). `customPrettifiers` option provides the ability to add a custom prettify function -for specific log properties. `customPrettifiers` is a `Map`, where keys are log -properties that will be prettified and value is the prettify function itself. +for specific log properties. `customPrettifiers` is an object, where keys are +log properties that will be prettified and value is the prettify function itself. For example, if a log line contains a `query` property, you can specify a prettifier for it: ```js { - customPrettifiers: new Map([ - ['query', prettifyQuery] - ]) + customPrettifiers: { + query: prettifyQuery + } } //... const prettifyQuery = value => { @@ -310,6 +310,15 @@ const prettifyQuery = value => { } ``` +```ts +// In TypeScript you have to use a Map +{ + customPrettifiers: new Map([ + ['query', prettifyQuery] + ]) +} +``` + All prettifiers use this function signature: ```js @@ -330,27 +339,27 @@ An example usage of `customPrettifiers` using all parameters from the function s ```js { - customPrettifiers: new Map([ + customPrettifiers: { // The argument for this function will be the same // string that's at the start of the log-line by default: - ['time', timestamp => `🕰 ${timestamp}`], + time: timestamp => `🕰 ${timestamp}`, // The argument for the level-prettifier may vary depending // on if the levelKey option is used or not. // By default this will be the same numerics as the Pino default: - ['level', logLevel => `LEVEL: ${logLevel}`], + level: logLevel => `LEVEL: ${logLevel}`, // level provides additional data in `extras`: // * label => derived level label string // * labelColorized => derived level label string with colorette colors applied based on customColors and whether colors are supported - ['level', (logLevel, key, log, { label, labelColorized, colors }) => `LEVEL: ${logLevel} LABEL: ${levelLabel} COLORIZED LABEL: ${labelColorized}`], + level: (logLevel, key, log, { label, labelColorized, colors }) => `LEVEL: ${logLevel} LABEL: ${levelLabel} COLORIZED LABEL: ${labelColorized}`, // other prettifiers can be used for the other keys if needed, for example - ['hostname', hostname => `MY HOST: ${hostname}`], - ['pid', pid => pid], - ['name', (name, key, log, { colors }) => `${colors.blue(name)}`], - ['caller', (caller, key, log, { colors }) => `${colors.greenBright(caller)}`], - ['myCustomLogProp', (value, key, log, { colors }) => `My Prop -> ${colors.bold(value)} <--`] - ]) + hostname: hostname => `MY HOST: ${hostname}`, + pid: pid => pid, + name: (name, key, log, { colors }) => `${colors.blue(name)}`, + caller: (caller, key, log, { colors }) => `${colors.greenBright(caller)}`, + myCustomLogProp: (value, key, log, { colors }) => `My Prop -> ${colors.bold(value)} <--` + } } ``` diff --git a/benchmark.js b/benchmark.js index 43825bc0..aaf7412e 100644 --- a/benchmark.js +++ b/benchmark.js @@ -51,14 +51,14 @@ const run = bench([ function customPrettifiers (cb) { const pretty = prettyFactory({ - customPrettifiers: new Map([ - ['time', (tstamp) => { + customPrettifiers: { + time (tstamp) { return tstamp - }], - ['pid', () => { + }, + pid () { return '' - }] - ]) + } + } }) const input = `{"time":${tstampMillis},"pid":1,"hostname":"foo","msg":"benchmark","foo":"foo","bar":{"bar":"bar"}}\n` for (var i = 0; i < max; i += 1) { diff --git a/index.js b/index.js index 3bfeecc2..a8aaadc3 100644 --- a/index.js +++ b/index.js @@ -30,7 +30,7 @@ const pretty = require('./lib/pretty') * to use for specific level labels, e.g. `err:red,info:blue`. * @property {string|null} [customLevels=null] A comma separated list of user * defined level names and numbers, e.g. `err:99,info:1`. - * @property {CustomPrettifiers} [customPrettifiers=Map] A set of prettifier + * @property {CustomPrettifiers} [customPrettifiers={}] A set of prettifier * functions to apply to keys defined in this object. * @property {K_ERROR_LIKE_KEYS} [errorLikeObjectKeys] A list of string property * names to consider as error objects. @@ -83,7 +83,7 @@ const defaultOptions = { crlf: false, customColors: null, customLevels: null, - customPrettifiers: new Map(), + customPrettifiers: {}, errorLikeObjectKeys: ERROR_LIKE_KEYS, errorProps: '', hideObject: false, @@ -110,6 +110,9 @@ const defaultOptions = { * @returns {LogPrettifierFunc} */ function prettyFactory (options) { + if (options.customPrettifiers instanceof Map) { + options.customPrettifiers = Object.fromEntries(options.customPrettifiers) + } const context = parseFactoryOptions(Object.assign({}, defaultOptions, options)) return pretty.bind({ ...context, context }) } diff --git a/lib/utils/index.js b/lib/utils/index.js index e596948b..259ccd92 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -34,13 +34,13 @@ module.exports = { // doc blocks, so we are picking this file as the answer. /** - * A map of log property names mapped to prettifier functions. When the + * A hash of log property names mapped to prettifier functions. When the * incoming log data is being processed for prettification, any key on the log - * that matches a key in a custom prettifiers map will be prettified using + * that matches a key in a custom prettifiers hash will be prettified using * that matching custom prettifier. The value passed to the custom prettifier * will the value associated with the corresponding log key. * - * The map may contain any arbitrary keys for arbitrary log properties, but it + * The hash may contain any arbitrary keys for arbitrary log properties, but it * may also contain a set of predefined key names that map to well-known log * properties. These keys are: * @@ -52,7 +52,7 @@ module.exports = { * + `name` * + `caller` * - * @typedef {Map} CustomPrettifiers + * @typedef {Object.|Map} CustomPrettifiers */ /** diff --git a/lib/utils/parse-factory-options.js b/lib/utils/parse-factory-options.js index 8fc327bf..3b051f03 100644 --- a/lib/utils/parse-factory-options.js +++ b/lib/utils/parse-factory-options.js @@ -25,7 +25,7 @@ const handleLevelLabelData = require('./get-level-label-data') * e.g. `{ 30: "info" }`. * @property {object} customLevels A hash of level names to level numbers, * e.g. `{ info: 30 }`. - * @property {CustomPrettifiers} customPrettifiers A map of custom prettifier + * @property {CustomPrettifiers} customPrettifiers A hash of custom prettifier * functions. * @property {object} customProperties Comprised of `customLevels` and * `customLevelNames` if such options are provided. diff --git a/lib/utils/prettify-error-log.test.js b/lib/utils/prettify-error-log.test.js index d1048002..04e10f82 100644 --- a/lib/utils/prettify-error-log.test.js +++ b/lib/utils/prettify-error-log.test.js @@ -10,7 +10,7 @@ const { const context = { EOL: '\n', IDENT: ' ', - customPrettifiers: new Map(), + customPrettifiers: {}, errorLikeObjectKeys: ERROR_LIKE_KEYS, errorProps: [], messageKey: MESSAGE_KEY diff --git a/lib/utils/prettify-level.js b/lib/utils/prettify-level.js index e401a38b..2e4dce84 100644 --- a/lib/utils/prettify-level.js +++ b/lib/utils/prettify-level.js @@ -29,7 +29,7 @@ function prettifyLevel ({ log, context }) { levelKey, getLevelLabelData } = context - const prettifier = context.customPrettifiers?.get('level') + const prettifier = context.customPrettifiers?.level const output = getPropertyValue(log, levelKey) if (output === undefined) return undefined const labelColorized = colorizer(output, { customLevels, customLevelNames }) diff --git a/lib/utils/prettify-level.test.js b/lib/utils/prettify-level.test.js index 1ce01089..e6e5a3c1 100644 --- a/lib/utils/prettify-level.test.js +++ b/lib/utils/prettify-level.test.js @@ -63,9 +63,7 @@ tap.test('passes output through provided prettifier', async t => { log, context: { ...context, - customPrettifiers: new Map([ - ['level', () => { return 'modified' }] - ]) + customPrettifiers: { level () { return 'modified' } } } }) t.equal(colorized, 'modified') diff --git a/lib/utils/prettify-metadata.js b/lib/utils/prettify-metadata.js index 0aab43fa..72483b14 100644 --- a/lib/utils/prettify-metadata.js +++ b/lib/utils/prettify-metadata.js @@ -28,14 +28,14 @@ function prettifyMetadata ({ log, context }) { line += '(' if (log.name) { - line += prettifiers.get('name') - ? prettifiers.get('name')(log.name, 'name', log, { colors: colorizer.colors }) + line += prettifiers.name + ? prettifiers.name(log.name, 'name', log, { colors: colorizer.colors }) : log.name } if (log.pid) { - const prettyPid = prettifiers.get('pid') - ? prettifiers.get('pid')(log.pid, 'pid', log, { colors: colorizer.colors }) + const prettyPid = prettifiers.pid + ? prettifiers.pid(log.pid, 'pid', log, { colors: colorizer.colors }) : log.pid if (log.name && log.pid) { line += '/' + prettyPid @@ -47,8 +47,8 @@ function prettifyMetadata ({ log, context }) { if (log.hostname) { // If `pid` and `name` were in the ignore keys list then we don't need // the leading space. - const prettyHostname = prettifiers.get('hostname') - ? prettifiers.get('hostname')(log.hostname, 'hostname', log, { colors: colorizer.colors }) + const prettyHostname = prettifiers.hostname + ? prettifiers.hostname(log.hostname, 'hostname', log, { colors: colorizer.colors }) : log.hostname line += `${line === '(' ? 'on' : ' on'} ${prettyHostname}` @@ -58,8 +58,8 @@ function prettifyMetadata ({ log, context }) { } if (log.caller) { - const prettyCaller = prettifiers.get('caller') - ? prettifiers.get('caller')(log.caller, 'caller', log, { colors: colorizer.colors }) + const prettyCaller = prettifiers.caller + ? prettifiers.caller(log.caller, 'caller', log, { colors: colorizer.colors }) : log.caller line += `${line === '' ? '' : ' '}<${prettyCaller}>` diff --git a/lib/utils/prettify-metadata.test.js b/lib/utils/prettify-metadata.test.js index a95a8c9c..cf2f892c 100644 --- a/lib/utils/prettify-metadata.test.js +++ b/lib/utils/prettify-metadata.test.js @@ -4,7 +4,7 @@ const tap = require('tap') const prettifyMetadata = require('./prettify-metadata') const getColorizer = require('../colors') const context = { - customPrettifiers: new Map(), + customPrettifiers: {}, colorizer: { colors: {} } @@ -91,19 +91,20 @@ tap.test('works with all four present', async t => { }) tap.test('uses prettifiers from passed prettifiers object', async t => { - const prettifiers = new Map() - prettifiers.set('name', (input) => { - return input.toUpperCase() - }) - prettifiers.set('pid', (input) => { - return input + '__' - }) - prettifiers.set('hostname', (input) => { - return input.toUpperCase() - }) - prettifiers.set('caller', (input) => { - return input.toUpperCase() - }) + const prettifiers = { + name (input) { + return input.toUpperCase() + }, + pid (input) { + return input + '__' + }, + hostname (input) { + return input.toUpperCase() + }, + caller (input) { + return input.toUpperCase() + } + } const str = prettifyMetadata({ log: { pid: '1234', hostname: 'bar', caller: 'baz', name: 'joe' }, context: { @@ -115,19 +116,20 @@ tap.test('uses prettifiers from passed prettifiers object', async t => { }) tap.test('uses colorizer from passed context to colorize metadata', async t => { - const prettifiers = new Map() - prettifiers.set('name', (input, _key, _log, { colors }) => { - return colors.blue(input) - }) - prettifiers.set('pid', (input, _key, _log, { colors }) => { - return colors.red(input) - }) - prettifiers.set('hostname', (input, _key, _log, { colors }) => { - return colors.green(input) - }) - prettifiers.set('caller', (input, _key, _log, { colors }) => { - return colors.cyan(input) - }) + const prettifiers = { + name (input, _key, _log, { colors }) { + return colors.blue(input) + }, + pid (input, _key, _log, { colors }) { + return colors.red(input) + }, + hostname (input, _key, _log, { colors }) { + return colors.green(input) + }, + caller (input, _key, _log, { colors }) { + return colors.cyan(input) + } + } const log = { name: 'foo', pid: '1234', hostname: 'bar', caller: 'baz' } const colorizer = getColorizer(true) const context = { diff --git a/lib/utils/prettify-object.js b/lib/utils/prettify-object.js index 5f1ecea5..5de0f5cb 100644 --- a/lib/utils/prettify-object.js +++ b/lib/utils/prettify-object.js @@ -57,8 +57,8 @@ function prettifyObject ({ const { plain, errors } = Object.entries(log).reduce(({ plain, errors }, [k, v]) => { if (keysToIgnore.includes(k) === false) { // Pre-apply custom prettifiers, because all 3 cases below will need this - const pretty = typeof customPrettifiers.get(k) === 'function' - ? customPrettifiers.get(k)(v, k, log, { colors: colorizer.colors }) + const pretty = typeof customPrettifiers[k] === 'function' + ? customPrettifiers[k](v, k, log, { colors: colorizer.colors }) : v if (errorLikeKeys.includes(k)) { errors[k] = pretty @@ -82,7 +82,7 @@ function prettifyObject ({ // Put each object entry on its own line Object.entries(plain).forEach(([keyName, keyValue]) => { // custom prettifiers are already applied above, so we can skip it now - let lines = typeof customPrettifiers.get(keyName) === 'function' + let lines = typeof customPrettifiers[keyName] === 'function' ? keyValue : stringifySafe(keyValue, null, 2) @@ -99,7 +99,7 @@ function prettifyObject ({ // Errors Object.entries(errors).forEach(([keyName, keyValue]) => { // custom prettifiers are already applied above, so we can skip it now - const lines = typeof customPrettifiers.get(keyName) === 'function' + const lines = typeof customPrettifiers[keyName] === 'function' ? keyValue : stringifySafe(keyValue, null, 2) diff --git a/lib/utils/prettify-object.test.js b/lib/utils/prettify-object.test.js index a3ea87b8..2dde4a9a 100644 --- a/lib/utils/prettify-object.test.js +++ b/lib/utils/prettify-object.test.js @@ -11,7 +11,7 @@ const getColorizer = require('../colors') const context = { EOL: '\n', IDENT: ' ', - customPrettifiers: new Map(), + customPrettifiers: {}, errorLikeObjectKeys: ERROR_LIKE_KEYS, objectColorizer: colors(), singleLine: false, @@ -76,9 +76,9 @@ tap.test('works with error props', async t => { }) tap.test('customPrettifiers gets applied', async t => { - const customPrettifiers = new Map([ - ['foo', v => v.toUpperCase()] - ]) + const customPrettifiers = { + foo: v => v.toUpperCase() + } const str = prettifyObject({ log: { foo: 'foo' }, context: { @@ -90,9 +90,9 @@ tap.test('customPrettifiers gets applied', async t => { }) tap.test('skips lines omitted by customPrettifiers', async t => { - const customPrettifiers = new Map([ - ['foo', () => { return undefined }] - ]) + const customPrettifiers = { + foo: () => { return undefined } + } const str = prettifyObject({ log: { foo: 'foo', bar: 'bar' }, context: { @@ -110,9 +110,9 @@ tap.test('joined lines omits starting eol', async t => { context: { ...context, IDENT: '', - customPrettifiers: new Map([ - ['calls', val => '\n' + val.map(it => ' ' + it).join('\n')] - ]) + customPrettifiers: { + calls: val => '\n' + val.map(it => ' ' + it).join('\n') + } } }) t.equal(str, [ @@ -126,9 +126,9 @@ tap.test('joined lines omits starting eol', async t => { }) tap.test('errors skips prettifiers', async t => { - const customPrettifiers = new Map([ - ['err', () => { return 'is_err' }] - ]) + const customPrettifiers = { + err: () => { return 'is_err' } + } const str = prettifyObject({ log: { err: Error('boom') }, context: { @@ -140,9 +140,9 @@ tap.test('errors skips prettifiers', async t => { }) tap.test('errors skips prettifying if no lines are present', async t => { - const customPrettifiers = new Map([ - ['err', () => { return undefined }] - ]) + const customPrettifiers = { + err: () => { return undefined } + } const str = prettifyObject({ log: { err: Error('boom') }, context: { diff --git a/lib/utils/prettify-time.js b/lib/utils/prettify-time.js index 6f4fa95a..e876b35f 100644 --- a/lib/utils/prettify-time.js +++ b/lib/utils/prettify-time.js @@ -26,7 +26,7 @@ function prettifyTime ({ log, context }) { timestampKey, translateTime: translateFormat } = context - const prettifier = context.customPrettifiers?.get('time') + const prettifier = context.customPrettifiers?.time let time = null if (timestampKey in log) { diff --git a/lib/utils/prettify-time.test.js b/lib/utils/prettify-time.test.js index 44ec8ebb..cfde6627 100644 --- a/lib/utils/prettify-time.test.js +++ b/lib/utils/prettify-time.test.js @@ -10,7 +10,7 @@ const { const context = { timestampKey: TIMESTAMP_KEY, translateTime: true, - customPrettifiers: new Map() + customPrettifiers: {} } tap.test('returns `undefined` if `time` or `timestamp` not in log', async t => { @@ -216,9 +216,11 @@ tap.test('uses custom prettifier', async t => { log: { time: 0 }, context: { ...context, - customPrettifiers: new Map([ - ['time', () => { return 'done' }] - ]) + customPrettifiers: { + time () { + return 'done' + } + } } }) t.equal(str, 'done') diff --git a/test/basic.test.js b/test/basic.test.js index 107ddb09..c5f61bb9 100644 --- a/test/basic.test.js +++ b/test/basic.test.js @@ -22,6 +22,8 @@ process.removeAllListeners('warning') function prettyFactory (opts) { if (!opts) { opts = { colorize: false } + } else if (opts instanceof Map && !opts.has('colorize')) { + opts.set('colorize', false) } else if (!Object.prototype.hasOwnProperty.call(opts, 'colorize')) { opts.colorize = false } @@ -240,9 +242,9 @@ test('basic prettifier tests', (t) => { 30: 'ok', 40: 'not great' } - const customPrettifiers = new Map([ - ['level', (level) => `LEVEL: ${veryCustomLevels[level]}`] - ]) + const customPrettifiers = { + level: (level) => `LEVEL: ${veryCustomLevels[level]}` + } const pretty = prettyFactory({ customPrettifiers }) const log = pino({}, new Writable({ write (chunk, enc, cb) { @@ -257,11 +259,30 @@ test('basic prettifier tests', (t) => { log.info({ msg: 'foo' }) }) - t.test('can use a customPrettifier on different-level-key output', (t) => { + t.test('can use a Map customPrettifier', (t) => { t.plan(1) const customPrettifiers = new Map([ - ['level', (level) => `LEVEL: ${level.toUpperCase()}`] + ['level', () => 'LEVEL: bar'] ]) + const pretty = prettyFactory({ customPrettifiers }) + const log = pino({}, new Writable({ + write (chunk, enc, cb) { + const formatted = pretty(chunk.toString()) + t.equal( + formatted, + `[${formattedEpoch}] LEVEL: bar (${pid}): foo\n` + ) + cb() + } + })) + log.info({ msg: 'foo' }) + }) + + t.test('can use a customPrettifier on different-level-key output', (t) => { + t.plan(1) + const customPrettifiers = { + level: (level) => `LEVEL: ${level.toUpperCase()}` + } const pretty = prettyFactory({ levelKey: 'bar', customPrettifiers }) const log = pino({}, new Writable({ write (chunk, enc, cb) { @@ -278,14 +299,11 @@ test('basic prettifier tests', (t) => { t.test('can use a customPrettifier to get final level label (no color)', (t) => { t.plan(1) - const customPrettifiers = new Map([ - [ - 'level', - (level, key, logThis, { label }) => { - return `LEVEL: ${label}` - } - ] - ]) + const customPrettifiers = { + level: (level, key, logThis, { label }) => { + return `LEVEL: ${label}` + } + } const pretty = prettyFactory({ customPrettifiers, colorize: false, useOnlyCustomProps: false }) const log = pino({}, new Writable({ write (chunk, enc, cb) { @@ -302,14 +320,11 @@ test('basic prettifier tests', (t) => { t.test('can use a customPrettifier to get final level label (colorized)', (t) => { t.plan(1) - const customPrettifiers = new Map([ - [ - 'level', - (level, key, logThis, { label, labelColorized }) => { - return `LEVEL: ${labelColorized}` - } - ] - ]) + const customPrettifiers = { + level: (level, key, logThis, { label, labelColorized }) => { + return `LEVEL: ${labelColorized}` + } + } const pretty = prettyFactory({ customPrettifiers, colorize: true, useOnlyCustomProps: false }) const log = pino({}, new Writable({ write (chunk, enc, cb) { @@ -326,9 +341,9 @@ test('basic prettifier tests', (t) => { t.test('can use a customPrettifier on name output', (t) => { t.plan(1) - const customPrettifiers = new Map([ - ['name', (hostname) => `NAME: ${hostname}`] - ]) + const customPrettifiers = { + name: (hostname) => `NAME: ${hostname}` + } const pretty = prettyFactory({ customPrettifiers }) const log = pino({}, new Writable({ write (chunk, enc, cb) { @@ -346,10 +361,10 @@ test('basic prettifier tests', (t) => { t.test('can use a customPrettifier on hostname and pid output', (t) => { t.plan(1) - const customPrettifiers = new Map([ - ['hostname', (hostname) => `HOSTNAME: ${hostname}`], - ['pid', (pid) => `PID: ${pid}`] - ]) + const customPrettifiers = { + hostname: (hostname) => `HOSTNAME: ${hostname}`, + pid: (pid) => `PID: ${pid}` + } const pretty = prettyFactory({ customPrettifiers, ignore: '' }) const log = pino({}, new Writable({ write (chunk, enc, cb) { @@ -366,9 +381,9 @@ test('basic prettifier tests', (t) => { t.test('can use a customPrettifier on default time output', (t) => { t.plan(1) - const customPrettifiers = new Map([ - ['time', (timestamp) => `TIME: ${timestamp}`] - ]) + const customPrettifiers = { + time: (timestamp) => `TIME: ${timestamp}` + } const pretty = prettyFactory({ customPrettifiers }) const log = pino({}, new Writable({ write (chunk, enc, cb) { @@ -385,9 +400,9 @@ test('basic prettifier tests', (t) => { t.test('can use a customPrettifier on the caller', (t) => { t.plan(1) - const customPrettifiers = new Map([ - ['caller', (caller) => `CALLER: ${caller}`] - ]) + const customPrettifiers = { + caller: (caller) => `CALLER: ${caller}` + } const pretty = prettyFactory({ customPrettifiers }) const log = pino({}, new Writable({ write (chunk, enc, cb) { @@ -404,9 +419,9 @@ test('basic prettifier tests', (t) => { t.test('can use a customPrettifier on translateTime-time output', (t) => { t.plan(1) - const customPrettifiers = new Map([ - ['time', (timestamp) => `TIME: ${timestamp}`] - ]) + const customPrettifiers = { + time: (timestamp) => `TIME: ${timestamp}` + } const pretty = prettyFactory({ customPrettifiers, translateTime: true }) const log = pino({}, new Writable({ write (chunk, enc, cb) { @@ -830,10 +845,10 @@ test('basic prettifier tests', (t) => { t.test('prettifies custom key', (t) => { t.plan(1) const pretty = prettyFactory({ - customPrettifiers: new Map([ - ['foo', val => `${val}_baz\nmultiline`], - ['cow', val => val.toUpperCase()] - ]) + customPrettifiers: { + foo: val => `${val}_baz\nmultiline`, + cow: val => val.toUpperCase() + } }) const arst = pretty('{"msg":"hello world", "foo": "bar", "cow": "moo", "level":30}') t.equal(arst, 'INFO: hello world\n foo: bar_baz\n multiline\n cow: MOO\n') @@ -842,9 +857,9 @@ test('basic prettifier tests', (t) => { t.test('does not add trailing space if prettified value begins with eol', (t) => { t.plan(1) const pretty = prettyFactory({ - customPrettifiers: new Map([ - ['calls', val => '\n' + val.map(it => ' ' + it).join('\n')] - ]) + customPrettifiers: { + calls: val => '\n' + val.map(it => ' ' + it).join('\n') + } }) const arst = pretty('{"msg":"doing work","calls":["step 1","step 2","step 3"],"level":30}') t.equal(arst, 'INFO: doing work\n calls:\n step 1\n step 2\n step 3\n') @@ -853,10 +868,10 @@ test('basic prettifier tests', (t) => { t.test('does not prettify custom key that does not exists', (t) => { t.plan(1) const pretty = prettyFactory({ - customPrettifiers: new Map([ - ['foo', val => `${val}_baz`], - ['cow', val => val.toUpperCase()] - ]) + customPrettifiers: { + foo: val => `${val}_baz`, + cow: val => val.toUpperCase() + } }) const arst = pretty('{"msg":"hello world", "foo": "bar", "level":30}') t.equal(arst, 'INFO: hello world\n foo: bar_baz\n') @@ -1060,10 +1075,10 @@ test('basic prettifier tests', (t) => { const pretty = prettyFactory({ singleLine: true, colorize: false, - customPrettifiers: new Map([ - ['upper', val => val.toUpperCase()], - ['undef', () => undefined] - ]) + customPrettifiers: { + upper: val => val.toUpperCase(), + undef: () => undefined + } }) const log = pino({}, new Writable({ write (chunk, enc, cb) { @@ -1165,10 +1180,10 @@ test('basic prettifier tests', (t) => { mkdir: true, append: false, destination: new SonicBoom({ dest: destination, async: false, mkdir: true, append: true }), - customPrettifiers: new Map([ - ['upper', val => val.toUpperCase()], - ['undef', () => undefined] - ]) + customPrettifiers: { + upper: val => val.toUpperCase(), + undef: () => undefined + } }) const log = pino(pretty) log.info({ msg: 'message', extra: { foo: 'bar', number: 42 }, upper: 'foobar', undef: 'this will not show up' }) diff --git a/test/error-objects.test.js b/test/error-objects.test.js index 653f3a47..b9840c6c 100644 --- a/test/error-objects.test.js +++ b/test/error-objects.test.js @@ -151,9 +151,9 @@ test('error like objects tests', (t) => { t.plan(4) const pretty = prettyFactory({ errorLikeObjectKeys: ['err'], - customPrettifiers: new Map([ - ['err', val => `error is ${val.message}`] - ]) + customPrettifiers: { + err: val => `error is ${val.message}` + } }) const err = Error('hello world')