Skip to content

Commit

Permalink
feat!: change customPrettifiers to a Map
Browse files Browse the repository at this point in the history
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] #551
[2] #550
  • Loading branch information
Frederick888 committed Jan 14, 2025
1 parent 6da74ee commit 38cdaf2
Show file tree
Hide file tree
Showing 18 changed files with 180 additions and 173 deletions.
32 changes: 16 additions & 16 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
```

Expand All @@ -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 => {
Expand Down Expand Up @@ -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)} <--`]
])
}
```

Expand Down
12 changes: 6 additions & 6 deletions benchmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
23 changes: 11 additions & 12 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,28 +165,25 @@ 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 => {
* // do some prettify magic
* }
* ```
*/
customPrettifiers?: Record<string, Prettifier> &
{
level?: Prettifier
};
customPrettifiers?: CustomPrettifiers;
/**
* Change the level names and values to an user custom preset.
*
Expand Down Expand Up @@ -218,16 +215,18 @@ 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<LevelPrettifierExtras>> & Map<string, Prettifier>
type Prettifier<T = object> = (inputData: string | object, key: string, log: object, extras: PrettifierExtras<T>) => string;
type PrettifierExtras<T = object> = {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;
type PrettyFactory = typeof prettyFactory;
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;
4 changes: 2 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -83,7 +83,7 @@ const defaultOptions = {
crlf: false,
customColors: null,
customLevels: null,
customPrettifiers: {},
customPrettifiers: new Map(),
errorLikeObjectKeys: ERROR_LIKE_KEYS,
errorProps: '',
hideObject: false,
Expand Down
8 changes: 4 additions & 4 deletions lib/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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:
*
Expand All @@ -52,7 +52,7 @@ module.exports = {
* + `name`
* + `caller`
*
* @typedef {Object.<string, CustomPrettifierFunc>} CustomPrettifiers
* @typedef {Map<string, CustomPrettifierFunc>} CustomPrettifiers
*/

/**
Expand Down
2 changes: 1 addition & 1 deletion lib/utils/parse-factory-options.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion lib/utils/prettify-error-log.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const {
const context = {
EOL: '\n',
IDENT: ' ',
customPrettifiers: {},
customPrettifiers: new Map(),
errorLikeObjectKeys: ERROR_LIKE_KEYS,
errorProps: [],
messageKey: MESSAGE_KEY
Expand Down
2 changes: 1 addition & 1 deletion lib/utils/prettify-level.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 })
Expand Down
4 changes: 3 additions & 1 deletion lib/utils/prettify-level.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
16 changes: 8 additions & 8 deletions lib/utils/prettify-metadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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}`
Expand All @@ -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}>`
Expand Down
56 changes: 27 additions & 29 deletions lib/utils/prettify-metadata.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: {}
}
Expand Down Expand Up @@ -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: {
Expand All @@ -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 = {
Expand Down
8 changes: 4 additions & 4 deletions lib/utils/prettify-object.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)

Expand All @@ -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)

Expand Down
Loading

0 comments on commit 38cdaf2

Please sign in to comment.