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');
+ },
},
});