Repository: pinojs/pino
Pull request #1541 (2022-08-31 10:08:41)

Added `onChild` callback with tests and documentation
As discussed in this issue https://github.com/pinojs/pino/issues/1534, i've added an `onChild` callback that uses the child instance as its argument.
I also included tests and documentation.

Please let me know if anything should be change :)

Diabl0269: @mcollina - I see some tests are failing on the CI.
The benchmark seems like it just hangs, on my machine it passes fairly quick.
I also checked macOS with version 18 and it passes, about windows I can't currently check locally.
What should I do about these?
Diabl0269: Thanks you!
First time I use `tsd` so I hope I did it right.
Over then that I updated the `onChild` type to recognize it as an initialization option as well as a property on the logger. (The JS actually works for both but the type was missing).
Diabl0269: Thanks again!
I don't have permission to merge the PR, will you give me the permissions/merge it?
mcollina: Let's wait a few more reviews, then I'll merge and ship this.

Patch:
diff --git a/.github/workflows/lock-threads.yml b/.github/workflows/lock-threads.yml
index 91f5d51..78d510f 100644
--- a/.github/workflows/lock-threads.yml
+++ b/.github/workflows/lock-threads.yml
@@ -16,7 +16,7 @@ jobs:
   action:
     runs-on: ubuntu-latest
     steps:
-      - uses: jsumners/lock-threads@e460dfeb36e731f3aeb214be6b0c9a9d9a67eda6
+      - uses: jsumners/lock-threads@b27edac0ac998d42b2815e122b6c24b32b568321
         with:
           log-output: true
           issue-inactive-days: '30'
diff --git a/.github/workflows/package-manager-ci.yml b/.github/workflows/package-manager-ci.yml
index a1a7b14..860d73d 100644
--- a/.github/workflows/package-manager-ci.yml
+++ b/.github/workflows/package-manager-ci.yml
@@ -4,6 +4,8 @@ on:
     branches:
       - main
       - package-manager-load
+permissions:
+  contents: read
 jobs:
   pnpm:
     name: pnpm package manager on ${{ matrix.node-version }} ${{ matrix.os }}
diff --git a/docs/api.md b/docs/api.md
index 7c2a784..8e0004c 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -507,6 +507,19 @@ when using the `transport` option. In this case an `Error` will be thrown.
 
 * See [pino.transport()](#pino-transport)
 
+#### `onChild` (Function)
+
+The `onChild` function is a synchronous callback that will be called on each creation of a new child, passing the child instance as its first argument.  
+Any error thrown inside the callback will be uncaught and should be handled inside the callback.
+```js
+const parent = require('pino')({ onChild: (instance) => {
+  // Exceute call back code for each newly created child.
+}})
+// `onChild` will now be executed with the new child.
+parent.child(bindings)
+```
+
+
 <a id="destination"></a>
 ### `destination` (SonicBoom | WritableStream | String | Object)
 
diff --git a/docs/help.md b/docs/help.md
index 6cac1c0..3760eef 100644
--- a/docs/help.md
+++ b/docs/help.md
@@ -12,7 +12,6 @@
 * [Unicode and Windows terminal](#windows)
 * [Mapping Pino Log Levels to Google Cloud Logging (Stackdriver) Severity Levels](#stackdriver)
 * [Avoid Message Conflict](#avoid-message-conflict)
-* [Exit logging](#exit-logging)
 
 <a id="rotate"></a>
 ## Log rotation
diff --git a/lib/proto.js b/lib/proto.js
index 25c0c5b..4009452 100644
--- a/lib/proto.js
+++ b/lib/proto.js
@@ -141,7 +141,7 @@ function child (bindings, options) {
   instance[chindingsSym] = asChindings(instance, bindings)
   const childLevel = options.level || this.level
   instance[setLevelSym](childLevel)
-
+  this.onChild(instance)
   return instance
 }
 
diff --git a/lib/tools.js b/lib/tools.js
index 3ac99c7..674198b 100644
--- a/lib/tools.js
+++ b/lib/tools.js
@@ -298,8 +298,9 @@ function createArgsNormalizer (defaultOptions) {
       throw new Error('prettyPrint option is no longer supported, see the pino-pretty package (https://github.com/pinojs/pino-pretty)')
     }
 
-    const { enabled } = opts
+    const { enabled, onChild } = opts
     if (enabled === false) opts.level = 'silent'
+    if (!onChild) opts.onChild = noop
     if (!stream) {
       if (!hasBeenTampered(process.stdout)) {
         // If process.stdout.fd is undefined, it means that we are running
diff --git a/pino.d.ts b/pino.d.ts
index 6ad74ba..8e7fe6e 100644
--- a/pino.d.ts
+++ b/pino.d.ts
@@ -36,6 +36,12 @@ type MixinMergeStrategyFn = (mergeObject: object, mixinObject: object) => object
 
 type CustomLevelLogger<Options> = Options extends { customLevels: Record<string, number> } ? Record<keyof Options["customLevels"], LogFn> : Record<never, LogFn>
 
+/**
+* A synchronous callback that will run on each creation of a new child.
+* @param child: The newly created child logger instance.
+*/
+type OnChildCallback<Options = LoggerOptions> = <ChildOptions extends pino.ChildLoggerOptions>(child: pino.Logger<Options & ChildOptions>) => void
+
 interface redactOptions {
     paths: string[];
     censor?: string | ((value: any, path: string[]) => any);
@@ -79,6 +85,11 @@ interface LoggerExtras<Options = LoggerOptions> extends EventEmitter {
      */
     child<ChildOptions extends pino.ChildLoggerOptions>(bindings: pino.Bindings, options?: ChildOptions): pino.Logger<Options & ChildOptions>;
 
+    /**
+     * This can be used to modify the callback function on creation of a new child.
+     */
+    onChild: OnChildCallback<Options>;
+
     /**
      * Registers a listener function that is triggered when the level is changed.
      * Note: When browserified, this functionality will only be available if the `events` module has been required elsewhere
@@ -601,6 +612,11 @@ declare namespace pino {
           * Stringification limit of properties/elements when logging a specific object/array with circular references. Default: `100`.
           */
           edgeLimit?: number
+
+        /**
+         * Optional child creation callback.
+         */
+        onChild?: OnChildCallback;
     }
 
     interface ChildLoggerOptions {
diff --git a/pino.js b/pino.js
index 9ace3a7..934eb74 100644
--- a/pino.js
+++ b/pino.js
@@ -102,7 +102,8 @@ function pino (...args) {
     formatters,
     hooks,
     depthLimit,
-    edgeLimit
+    edgeLimit,
+    onChild
   } = opts
 
   const stringifySafe = configure({
@@ -175,7 +176,8 @@ function pino (...args) {
     [chindingsSym]: chindings,
     [formattersSym]: allFormatters,
     [hooksSym]: hooks,
-    silent: noop
+    silent: noop,
+    onChild
   })
 
   Object.setPrototypeOf(instance, proto())


Test patch:
diff --git a/test/basic.test.js b/test/basic.test.js
index 2f9700f..ca4ca1e 100644
--- a/test/basic.test.js
+++ b/test/basic.test.js
@@ -725,3 +725,13 @@ test('throws if prettyPrint is passed in as an option', async (t) => {
     })
   }, new Error('prettyPrint option is no longer supported, see the pino-pretty package (https://github.com/pinojs/pino-pretty)'))
 })
+
+test('Should invoke `onChild` with the newly created child', async ({ equal }) => {
+  let innerChild
+  const child = pino({
+    onChild: (instance) => {
+      innerChild = instance
+    }
+  }).child({ foo: 'bar' })
+  equal(child, innerChild)
+})
diff --git a/test/types/pino.test-d.ts b/test/types/pino.test-d.ts
index b2a6515..57b8ca4 100644
--- a/test/types/pino.test-d.ts
+++ b/test/types/pino.test-d.ts
@@ -340,3 +340,8 @@ cclog3.myLevel('')
 cclog3.childLevel('')
 // child itself
 cclog3.childLevel2('')
+
+const withChildCallback = pino({
+    onChild: (child: Logger) => {}
+})
+withChildCallback.onChild = (child: Logger) => {}
\ No newline at end of file