Good logging hygiene in Nest.js
30 December 2024 | Eric
Yes, I know a lot of you still use console.log
to debug your application. This is not so surprising as it is
straightforward to use and provide immediate feedback. The issue is that more often than we would like, some of the
logs end up in production where they pollute the system with useless information.
Logging is also important when it comes to analysis of failures. You can read a great article about this on Daniel Gerlach’s blog.
In his article, I particularly like this statement:
Rule of Silence: When a program has nothing surprising to say, it should say nothing.
Having a good logging hygiene is important and logging everything will certainly spam your log stream. In the end you won’t read it anymore. Instead, what we are aiming for is to log specifically what we want to see, like errors, or warnings, and avoid having debug information in production.
Let’s see how to do this in Nest.js! 🚀
Set the log level properly
Nest.js allows to set the log level during the application bootstrap. This is great because as we said we probably don’t need debug logs in production, but we still want to keep them in our code for the convenience of debugging when we work on your project.
Let’s do that in main.ts
:
const logger: LogLevel[] = process.env.NODE_ENV === 'development'
? ['fatal', 'error', 'warn', 'log', 'debug']
: ['fatal', 'error', 'warn', 'log'];
const app = await NestFactory.create(AppModule, { logger });
Simply, you define the levels you want to see and nothing else will show. Here, we show only fatal
, error
, warn
,
and log
in production but we also show debug
while developing locally.
FastifyAdapter
If you use the Fastify adapter (you should!), it’s a bit different as Fastify uses Pino under the hood, but we want to disable it completely.
const logger: LogLevel[] = process.env.NODE_ENV === 'development'
? ['fatal', 'error', 'warn', 'log', 'debug']
: ['fatal', 'error', 'warn', 'log'];
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter({ logger: false }), // <- Disable Pino
{ logger }
);
NestCommander
NestCommander works exactly the same as the default ExpressAdapter
:
const logger: LogLevel[] = process.env.NODE_ENV === 'development'
? ['fatal', 'error', 'warn', 'log', 'debug']
: ['fatal', 'error', 'warn', 'log'];
await CommandFactory.run(AppModule, { logger });
Test AppController
You can use the test controller provided below to test the difference. Obviously, this works only if you use the
Nest.js Logger
. It doesn’t work with console.log
, so simply stop using it. 😉
@Controller({})
export class AppController {
logger = new Logger(AppController.name);
@Get("log")
getLog(): string {
this.logger.debug("LogLevel[debug]");
this.logger.verbose("LogLevel[verbose]");
this.logger.log("LogLevel[log]");
this.logger.warn("LogLevel[warn]");
this.logger.error("LogLevel[error]");
this.logger.fatal("LogLevel[fatal]");
}
}
The result will be something like this:
Enjoy logging! ❤️