NestJS tip: how to always use your custom logger
Tags: nestjs, node, programming, beginners
Published: 2023-01-15 18:22:36

## TL;DR

> for NestJS v8, v9 and v10

Since we always want to use our own logger instead of falling back to Nest's native logger in case of initialization errors, you'll need to create the logger instance outside of your root module like this:

```ts
// ...
async function createLogger() {
  @Module({
    imports: [LoggerModule.forRoot()],
  })
  class TempModule {}

  const tempApp = await NestFactory.createApplicationContext(TempModule, {
    logger: false,
    abortOnError: false,
  })
  await tempApp.close()
  return tempApp.get(Logger)
}

async function bootstrap() {
  const app = await NestFactory.create(AppModule, {
    logger: await createLogger(),
  })
  // ...
  await app.listen(3000)
}
```

## Odd behavior for custom loggers that are defined too late

When we replace Nest's native logger with another one (like [`nestjs-pino`](https://npmjs.com/nestjs-pino)), we might still could face log messages that uses the native one due to some error on creating the application!

Check this out:

```ts
import { Module } from '@nestjs/common'
import { NestFactory } from '@nestjs/core'
import { Logger, LoggerModule } from 'nestjs-pino'

@Module({
  imports: [LoggerModule.forRoot()],
})
export class AppModule {
  // This will cause a 'provider not found' error
  constructor(private foo: number) {} 

  onModuleInit() {
    console.log('This will not be invoked due to the initialization error!')
  }
}


async function bootstrap() {
  const app = await NestFactory.create(AppModule, {
    bufferLogs: true, // So we could hijack our own logger
  })
  app.useLogger( app.get(Logger) ) // (A)
  await app.listen(3000)
}
bootstrap()
```

![error](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/45ej4u6hhxwfjmlnn6io.png)

As you can see above, although we're using our own logger (line `(A)`), since we got an initilization error, ie., the app couldn't be created due to some error while resolving its modules, the error log still uses the native logger.

When the app do start with success, the output would look like this:

![sucess](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/czga8tyrxixj6rimupmx.png)

### How to always use our custom logger

To fix that, we should supply our custom logger to the `logger` option of `NestFactory.create`. The limitation here is that that option requires an _instance_ of the logger, see:

![opts](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h7pmw9swy0rq67w6c9jl.png)

In order to create that instance using NestJS DI container, we'll use [`NestFactory.createApplicationContext`](https://docs.nestjs.com/faq/serverless#using-standalone-application-feature) method. Its has the same interface as the `NestFactory.create` method but it only returns the app context instead of the whole app itself, then it will take less steps to finish its job.

The root (or 'entry') module should be minimal as possible. In this example it will only import the module that creates the logger itself. I will use the `nestjs-pino` because [Pino](https://getpino.io) is an amazing logger lib.

Let's say we have the following `AppModule`:

```ts
import { Module } from '@nestjs/common'
import { LoggerModule } from 'nestjs-pino'

@Module({
  imports: [LoggerModule.forRoot()],
})
export class AppModule {}
```

- 1) Add a function that will start and return the logger instance created by NestJS DI system:

```ts
// ...
import { Logger, LoggerModule } from 'nestjs-pino'

async function createLogger(): Promise<Logger> {
  @Module({
    imports: [LoggerModule.forRoot()],
    //        ^ Make sure you're using the same logger config
    //          that you've used in your AppModule
  })
  class TempModule {}

  const tempApp = await NestFactory.createApplicationContext(TempModule, {
    // Disable the native logger entirely
    // to avoid falling back to the initial problem
    logger: false,
    // thus we must not crash the app on errors at TempModule
    // initialization, otherwise we won't see any error messages
    abortOnError: false,
  })
  await tempApp.close()
  return tempApp.get(Logger) // retrieve the created instance
}
```

- 2) Supply that logger instance to `NestFactory.create`:

```ts
async function bootstrap() {
  const app = await NestFactory.create(AppModule, {
    logger: await createLogger(),
  })

  await app.listen(3000)
}
bootstrap()
```

now there's no need to buffer logs (which is good for memory consumption btw), and no need to call `app.useLogger` too!

---

Now my app's initialization errors would look like this:

![better](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8yhuwahsdrymjv27iy8p.png)

or, with `pino-pretty`:

![better pretty](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f67trh2u57hnrp5648zd.png)