Repository: iamolegga/nestjs-pino (ts)

Issue:
[BUG] logger.assign does not add properties to "request completed" log.
[x] I've read [the docs of nestjs-pino](https://github.com/iamolegga/nestjs-pino/blob/master/README.md)

[x] I've read [the docs of pino](https://getpino.io/#/)

[x] I couldn't find the same [open issue of nestjs-pino](https://github.com/iamolegga/nestjs-pino/issues)

**What is the current behavior?**

When I call `this.logger.assign({ someKey: someValue })`, the key is properly added to the logs that belong to a request except the one that marks the end of the request "Request completed". 

**What is the expected behavior?**

I would expect that the method `this.logger.assign({ someKey: someValue })` adds the key and value to all the logs belonging to that request.

**Please mention other relevant information such as Node.js version and Operating System.**

I am using nestjs-pino 2.2.0.

Pull request description:
Feature/assign refactor
closes #608 

Patch:
diff --git a/README.md b/README.md
index 06c35bb..24e2786 100644
--- a/README.md
+++ b/README.md
@@ -378,21 +378,21 @@ With such change it's not possible to detect if method was called by app interna
 
 ## Assign extra fields for future calls
 
-You can enrich logs before calling log methods. It's possible by using `assign` method of `PinoLogger` instance. As `Logger` class is used only for NestJS built-in `Logger` substitution via `app.useLogger(...)` this feature is only limited to `PinoLogger` class. Example:
+You can enrich logs before calling log methods. It's possible by using `assign` function. Example:
 
 ```ts
+import { assign } from 'nestjs-pino';
 
 @Controller('/')
 class TestController {
   constructor(
-    private readonly logger: PinoLogger,
     private readonly service: MyService,
   ) {}
 
   @Get()
   get() {
     // assign extra fields in one place...
-    this.logger.assign({ userID: '42' });
+    assign({ userID: '42' });
     return this.service.test();
   }
 }
@@ -408,6 +408,8 @@ class MyService {
 }
 ```
 
+Due to the [limitation](https://github.com/pinojs/pino-http/issues/30) of the underlying `http-pino` `assign` cannot extend `Request completed` logs.
+
 ## Change pino params at runtime
 
 Pino root instance with passed via module registration params creates a separate child logger for every request. This root logger params can be changed at runtime via `PinoLogger.root` property which is the pointer to logger instance. Example:
diff --git a/src/PinoLogger.ts b/src/PinoLogger.ts
index f4622bd..1f6f88f 100644
--- a/src/PinoLogger.ts
+++ b/src/PinoLogger.ts
@@ -1,6 +1,7 @@
 /* eslint-disable @typescript-eslint/ban-types */
 import { Injectable, Inject, Scope } from '@nestjs/common';
 import * as pino from 'pino';
+import { assign } from './assign';
 import { Params, isPassedLogger, PARAMS_PROVIDER_TOKEN } from './params';
 import { storage } from './storage';
 
@@ -140,14 +141,12 @@ export class PinoLogger implements PinoMethods {
     return storage.getStore()?.logger || outOfContext!;
   }
 
-  public assign(fields: pino.Bindings) {
-    const store = storage.getStore();
-    if (!store) {
-      throw new Error(
-        `${PinoLogger.name}: unable to assign extra fields out of request scope`,
-      );
-    }
-    store.logger = store.logger.child(fields);
+  /**
+   * @deprecated in favour of `assign` function from the package.
+   * No need to inject `PinoLogger`
+   */
+  public assign(fields: pino.Bindings): void {
+    assign(fields);
   }
 }
 
diff --git a/src/assign.ts b/src/assign.ts
new file mode 100644
index 0000000..545d656
--- /dev/null
+++ b/src/assign.ts
@@ -0,0 +1,17 @@
+import * as pino from 'pino';
+import { storage } from './storage';
+import { PinoLogger } from './PinoLogger';
+
+/**
+ * assign replaces current logger with the child one containing passed fields
+ * @param fields
+ */
+export function assign(fields: pino.Bindings): void {
+  const store = storage.getStore();
+  if (!store) {
+    throw new Error(
+      `${PinoLogger.name}: unable to assign extra fields out of request scope`,
+    );
+  }
+  store.logger = store.logger.child(fields);
+}
diff --git a/src/index.ts b/src/index.ts
index 66a0532..23aeea6 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -7,3 +7,4 @@ export {
   LoggerModuleAsyncParams,
   PARAMS_PROVIDER_TOKEN,
 } from './params';
+export { assign } from './assign';


Test patch:
diff --git a/__tests__/assign.spec.ts b/__tests__/assign.spec.ts
index 2dd559b..23deb70 100644
--- a/__tests__/assign.spec.ts
+++ b/__tests__/assign.spec.ts
@@ -5,14 +5,14 @@ import {
   Logger,
   OnModuleInit,
 } from '@nestjs/common';
-import { PinoLogger } from '../src';
+import { assign, PinoLogger } from '../src';
 import { platforms } from './utils/platforms';
 import { TestCase } from './utils/test-case';
 
 describe('assign', () => {
   for (const PlatformAdapter of platforms) {
     describe(PlatformAdapter.name, () => {
-      it('in request context', async () => {
+      it('in request context via PinoLogger', async () => {
         const msg = Math.random().toString();
 
         @Injectable()
@@ -50,6 +50,41 @@ describe('assign', () => {
         expect(wanted).toBeTruthy();
       });
 
+      it('in request context via assign', async () => {
+        const msg = Math.random().toString();
+
+        @Injectable()
+        class TestService {
+          private readonly logger = new Logger(TestService.name);
+
+          test() {
+            this.logger.log(msg);
+            return {};
+          }
+        }
+
+        @Controller('/')
+        class TestController {
+          constructor(private readonly service: TestService) {}
+
+          @Get()
+          get() {
+            assign({ foo: 'bar' });
+            return this.service.test();
+          }
+        }
+
+        const logs = await new TestCase(new PlatformAdapter(), {
+          controllers: [TestController],
+          providers: [TestService],
+        })
+          .forRoot()
+          .run();
+
+        const wanted = logs.some((l) => l.msg === msg && l.foo === 'bar');
+        expect(wanted).toBeTruthy();
+      });
+
       it('out of request context', async () => {
         const msg = Math.random().toString();