Logging is an important aspect of the application development. Backend engineers depend on logging during the development. In production while the solution is up and running, logs are highly insightful and sometimes life saving to figure out production issues.
Logging with a strategy is essential to keep monitoring our apps for expected work flows, unidentified issues and fixing any bottlenecks.
Lesson Goal
We will add a custom Logging Functionality for a Nest JS Rest application. Let us generate a custom service to wrap the console.logs(..) to logs messages to the console and also writes them to a log file.
1. Define the Logger Service
Create a logger.service.ts file in the src folder (or wherever appropriate in your project structure).
import { Injectable } from '@nestjs/common';
@Injectable()
export class LoggerService {
constructor() {
}
}
2. Implement the Logger Service
- Console Logging: The
printToConsolemethod logs messages to the console with a specific log level (e.g.,INFO,ERROR). - File Logging: The
writeToFilemethod writes the log messages to a file. It uses thefs.appendFileSyncmethod to append log messages to theapplication.logfile. - Log Directory: The code ensures that the log directory (
logs/) exists and creates it if not. This is done usingfs.mkdirSyncwith the{ recursive: true }option. - Timestamp: Each log entry includes a timestamp in ISO format to mark when the log entry was created.
import { Injectable } from '@nestjs/common';
import * as fs from 'fs';
import * as path from 'path';
@Injectable()
export class LoggerService {
private logFilePath: string;
constructor() {
// Define the log file path
this.logFilePath = path.join(__dirname, '..', 'logs', 'application.log');
// Ensure the logs directory exists
if (!fs.existsSync(path.dirname(this.logFilePath))) {
fs.mkdirSync(path.dirname(this.logFilePath), { recursive: true });
}
}
log(message: string) {
this.printToConsole('INFO', message);
this.writeToFile('INFO', message);
}
error(message: string, trace: string) {
this.printToConsole('ERROR', `${message} \nTrace: ${trace}`);
this.writeToFile('ERROR', `${message} \nTrace: ${trace}`);
}
warn(message: string) {
this.printToConsole('WARN', message);
this.writeToFile('WARN', message);
}
debug(message: string) {
this.printToConsole('DEBUG', message);
this.writeToFile('DEBUG', message);
}
verbose(message: string) {
this.printToConsole('VERBOSE', message);
this.writeToFile('VERBOSE', message);
}
private printToConsole(level: string, message: string) {
console.log(`[${level}] ${message}`);
}
private writeToFile(level: string, message: string) {
const timestamp = new Date().toISOString();
const logMessage = `[${timestamp}] [${level}] ${message}\n`;
fs.appendFileSync(this.logFilePath, logMessage, 'utf8');
}
}
Step 3: Register the LoggerService in your module
To make CustomLoggerService available in your NestJS application, register it in your main module or any other relevant module.
import { Module } from '@nestjs/common';
import { CustomLoggerService } from './logger.service';
@Module({
providers: [CustomLoggerService],
exports: [CustomLoggerService],
})
export class LoggerModule {}
Step 4: Use the LoggerService in your application
You can now inject the LoggerService into any component, such as controllers or other services, to log messages.
import { Controller, Get } from '@nestjs/common';
import { LoggerService } from './logger.service';
@Controller('example')
export class ExampleController {
constructor(private readonly logger: LoggerService) {}
@Get()
getHello(): string {
this.logger.log('Request received in ExampleController');
this.logger.warn('This is a warning message');
this.logger.error('This is an error message', 'Error trace details...');
return 'Hello World';
}
}
When you run the NestJS application, you will see the logs in the console, and they will also be written to the logs/application.log file in the root of your project.
Great! In case this logger has to be used in multiple places in your project, it is advisable to import it gobally. Check Making a Nest JS Service as Global.
In some scenarios where logs of an application in production environment need to be shared with third parties, an advanced logging mechanism may be required. For example features like log rotation or asynchronous logging. You can also consider using a Third Party Logger Library that is suitable for your requirement.
