NestJS logs not matching Google Cloud Stackdriver verbosity levels

Viewed 278

I have a NestJS application that I am running on cloud run in Google Cloud (GCP). When viewing the logs in GCP from "Logs Explorer," I can see all log output from my NestJS app only when the default value is selected in the severity dropdown. When I change the severity level to filter certain verbosity levels, it does not apply the filter to the NestJS log output, it only shows the logs when I have the default log level selected.

For example, in my NestJS application, when I do a log.debug("hello world"), I can see "DEBUG" in my log output in Logs Explorer using the 'default' severity, but when I change the severity dropdown from default to debug, I am expecting to see only the NestJS logs that correspond to debug but it does not show any logs. The GCP severity dropdown filter does not map to the NestJS logs correctly from what I am seeing.

Is there a way to configure NestJS logs to match Stackdriver verbosity levels within GCP log viewer?enter image description here

1 Answers

You can do this by overriding the default logger with a customer logger and reformatting the logging string in such a way that the google log explorer will identify the severity and message separately.

Below is your custom logger


import { LoggerService } from "@nestjs/common";


export class CustomLogger implements LoggerService {

  log(message: any, ...optionalParams: any[]) {
    console.log(JSON.stringify({ "severity": "INFO", "message": message }));
  }

  /**
   * Write an "error" level log.
   */
  error(message: any, ...optionalParams: any[]) {
    console.log(JSON.stringify({"severity": "ERROR", "message": message }));
  }

  /**
   * Write a "warn" level log.
   */
  warn(message: any, ...optionalParams: any[]) {
    console.log(JSON.stringify({ "severity": "WARNING", "message": message }));
  }

  /**
   * Write a "debug" level log.
   */
  debug?(message: any, ...optionalParams: any[]) {
    console.log(JSON.stringify({ "severity": "DEBUG", "message": message }));
  }

  /**
   * Write a "verbose" level log.
   */
  verbose?(message: any, ...optionalParams: any[]) {
  }
}

Make sure you stringify the log message. otherwise, google log explorer cannot identify the log level and the message separately.

Add your custom logger to the Logger Module and make it global so that you can use it anywhere in your application.


import { Module, Global } from '@nestjs/common';
import { CustomLogger } from './logger.service';
import { LoggingInterceptor } from './logging.interceptor';

@Global()
@Module({
  providers: [CustomLogger, LoggingInterceptor],
  exports: [CustomLogger],
})
export class LoggerModule {
}

Then register the logger module when boostrapping the application


import { CustomLogger } from './logger/logger.service';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule, {
    bufferLogs: true,
  });
  app.useLogger(app.get(CustomLogger));
  await app.listen(PORT)
}

Make a logging interceptor and inject your logging service to that and you are done.


import { CustomLogger } from './logger.service';

@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  constructor(private customLogger: CustomLogger) {}
  intercept(context: ExecutionContext, next) {
    const now = Date.now();
    const req = context.switchToHttp().getRequest();
    const method = req.method;
    const url = req.url;

    return next.handle().pipe(
      tap(() => {
        this.customLogger.log("logging string");
      }),
    );
  }
}

you can customize the logging string as you wish.

Related