From bb1f89dbd9424edd3522022786316dadd97bb059 Mon Sep 17 00:00:00 2001 From: Theo Ephraim Date: Sat, 14 Dec 2024 08:17:17 -0800 Subject: [PATCH] add new streamWrite hook (#2105) --- docs/api.md | 17 +++++++++++++++++ lib/proto.js | 6 ++++-- pino.d.ts | 6 ++++++ pino.js | 3 ++- test/hooks.test.js | 21 +++++++++++++++++++++ test/types/pino.test-d.ts | 4 ++++ 6 files changed, 54 insertions(+), 3 deletions(-) diff --git a/docs/api.md b/docs/api.md index b3d5a0b6a..c68ba5cbd 100644 --- a/docs/api.md +++ b/docs/api.md @@ -375,6 +375,23 @@ const hooks = { } ``` + + +##### `streamWrite` + +Allows for manipulating the _stringified_ JSON log data just before writing to various transports. + +The method receives the stringified JSON and must return valid stringified JSON. + +For example: +```js +const hooks = { + streamWrite (s) { + return s.replaceAll('sensitive-api-key', 'XXX') + } +} +``` + #### `formatters` (Object) diff --git a/lib/proto.js b/lib/proto.js index ef49a7122..1ce47f6ea 100644 --- a/lib/proto.js +++ b/lib/proto.js @@ -27,7 +27,8 @@ const { stringifySym, formatOptsSym, stringifiersSym, - msgPrefixSym + msgPrefixSym, + hooksSym } = require('./symbols') const { getLevel, @@ -185,6 +186,7 @@ function write (_obj, msg, num) { const messageKey = this[messageKeySym] const mixinMergeStrategy = this[mixinMergeStrategySym] || defaultMixinMergeStrategy let obj + const streamWriteHook = this[hooksSym].streamWrite if (_obj === undefined || _obj === null) { obj = {} @@ -214,7 +216,7 @@ function write (_obj, msg, num) { stream.lastTime = t.slice(this[timeSliceIndexSym]) stream.lastLogger = this // for child loggers } - stream.write(s) + stream.write(streamWriteHook ? streamWriteHook(s) : s) } function noop () {} diff --git a/pino.d.ts b/pino.d.ts index cdc5958d3..87eebe38d 100644 --- a/pino.d.ts +++ b/pino.d.ts @@ -641,6 +641,12 @@ declare namespace pino { * using apply, like so: method.apply(this, newArgumentsArray). */ logMethod?: (this: Logger, args: Parameters, method: LogFn, level: number) => void; + + /** + * Allows for manipulating the stringified JSON log output just before writing to various transports. + * This function must return a string and must be valid JSON. + */ + streamWrite?: (s: string) => string; }; /** diff --git a/pino.js b/pino.js index 2398e5acb..cabeaf6d6 100644 --- a/pino.js +++ b/pino.js @@ -70,7 +70,8 @@ const defaultOptions = { } }), hooks: { - logMethod: undefined + logMethod: undefined, + streamWrite: undefined }, timestamp: epochTime, name: undefined, diff --git a/test/hooks.test.js b/test/hooks.test.js index c5a67b6d9..5ed89057f 100644 --- a/test/hooks.test.js +++ b/test/hooks.test.js @@ -95,3 +95,24 @@ tap.test('log method hook', t => { t.end() }) + +tap.test('streamWrite hook', t => { + t.test('gets invoked', async t => { + t.plan(1) + + const stream = sink() + const logger = pino({ + hooks: { + streamWrite (s) { + return s.replaceAll('redact-me', 'XXX') + } + } + }, stream) + + const o = once(stream, 'data') + logger.info('hide redact-me in this string') + t.match(await o, { msg: 'hide XXX in this string' }) + }) + + t.end() +}) diff --git a/test/types/pino.test-d.ts b/test/types/pino.test-d.ts index 02284ccbd..3c3d6e955 100644 --- a/test/types/pino.test-d.ts +++ b/test/types/pino.test-d.ts @@ -209,6 +209,10 @@ const withHooks = pino({ expectType(this); return method.apply(this, args); }, + streamWrite(s) { + expectType(s); + return s.replaceAll('secret-key', 'xxx'); + }, }, });