Exception Handling in Node.js Applications using log4js
Tags: node, api, errors, typescript
Published: 2023-06-02 04:57:57

Exception handling plays a crucial role in ensuring the stability and reliability of Node.js applications. By effectively handling exceptions, developers can identify and address errors, improve debugging, and provide a smoother user experience. In this blog post, we will explore how to handle exceptions in a Node.js application using the popular logging library, log4js with customized configurations.

When logging messages, it is important to easily identify the log level, such as whether it is an error, warning, or information.

To use the Log4Js package, follow these basic steps:

#### **1.Install the package by running the following command:**
```typescript 
npm install log4js
```

You can find more information about the Log4js package on the [npmjs.com](https://www.npmjs.com/package/log4js) website.

#### **2.Create a new file called logger.ts and add the following code:**
```typescript
// Description: This file contains the logger configuration and custom logger functions
import { getLogger, configure } from 'log4js';
//configure logger
//type of appenders: console, file, dateFile, logLevelFilter, multiFile, stdout, stderr, cluster, log4js
//level of appenders: all, trace, debug, info, warn, error, fatal, mark, off
configure(
    {
        appenders: {          
            console: { type: "console" }
        },
        categories: {
            default: {
                appenders: ["console"],
                level: 'all'

            }
        }
    }
);
//get logger
const logger = getLogger();
//custom error logger
const errorLogger = (error: Error, functionName:string)=>{

    let responseErrorMessage = error.message;
    logger.error(`Function Name: ${functionName} \n Message: ${responseErrorMessage} \n Stack: ${error.stack}`)
        
}
//custom info logger
const infoLogger = (infoMessage: string, functionName:string)=>{

    logger.info(`Function Name: ${functionName} Message: ${infoMessage}`);
       
}
//custom warning logger
const warnLogger = (warnMessage: string, functionName:string)=>{

    logger.warn(`Function Name: ${functionName} Warning: ${warnMessage}`);    

}
export default {errorLogger,infoLogger,warnLogger}
```
This code sets up a logger configuration and defines three custom logger functions: errorLogger, infoLogger, and warnLogger. Here's a breakdown of the code:

•	The code imports the getLogger and configure functions from the 'log4js' library.
•	The configure function is called to set up the logger configuration. In this configuration, a single appender of type "console" is defined
•	The logger configuration specifies that all log events should be appended to the console appender.
•	The **getLogger** function is called to retrieve a logger instance
•	The **errorLogger** function takes an **Error object** and a **functionName** string as parameters. It logs an error message, including the function name, error message, and stack trace.
•	The **infoLogger** function takes an **infoMessage** string and a **functionName** string as parameters. It logs an info message, including the function name and info message.
•	The **warnLogger** function takes a **warnMessage** string and a **functionName** string as parameters. It logs a warning message, including the function name and warning message
•	Finally, the code exports an object containing the three custom logger functions: errorLogger, infoLogger, and warnLogger.

Overall, this code sets up a logging configuration using the log4js library and provides custom logger functions for logging errors, info messages, and warnings with additional context information like function names.

#### **3.Use the logger in your function. Here is an example using logger.ts:**
It logs various messages at different stages of execution using a custom logger object.

```typescript
import { Request, Response } from "express";
import models from "../models"
import logger from "../log/logger";
const getContact = async (req: Request, res: Response) => {

    logger.infoLogger("Start", "getContact");

    let contactDetails: {count:number,
          rows:{
                 Id:string,  
                 LastName:string, 
                 MiddleName:string, 
                 FirstName:string, 
                 ContactNumber:string, 
                 Email:string}[]};
    try {
        const contactnumber=req.params.contactnumber
        contactDetails = await models.Contact.findAndCountAll({
            
            offset: 0,
            //limit: max,    
            attributes: [
            "Id",           
            "LastName",
            "MiddleName",
            "FirstName",            
            "ContactNumber",
            "Email"
            ],
            where: { IsActive: 1,ContactNumber:contactnumber }
        });
        if (contactDetails.count == 0) {
            logger.infoLogger("No data found", "getContact");
            logger.warnLogger( `No data found for contact number: ${contactnumber}`, "getContact");
            return res.status(204).json({ message: 'No data found' });
        }
        else
            return res.status(200).json({ data: contactDetails });
    }catch (error: any) {

        logger.errorLogger(error, "getContact");

        if (error.statusCode) {
            return res.status(error.statusCode).json({ message: error.message });
        } else {
            return res.status(500).json({message: `An unexpected error occurred. ${error.message}` });
        }

    }
}
```

#### **4.Below is the expected output in various scenarios:**

**• When the data not found:**
Logging level: Info
Color: **Green**
Output:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3gkcrzbhz4defrp7zxzb.png)
**• When an unexpected error occurs (catch block):**
Logging level: Error
Color: **Red**
Output:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dwwii0o4kkemv5d15r2u.png)
**• When a warning message is logged:**
Logging level: Warning
Color: **Yellow**
Output:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g09eb4nzcfp8xv79ackf.png)
To review logs on Azure, you can refer to the article [Exception Handling, Logging, and Notifications in Azure App Services: A Comprehensive Guide](https://dev.to/yogitakadam14/exception-handling-logging-and-notifications-in-azure-app-services-a-comprehensive-guide-45g5) for detailed instructions.

By following best practices and leveraging the power of log4js, developers can enhance their Node.js applications' overall quality and maintainability. Furthermore, the comprehensive guide on exception handling, logging, and notifications in Azure App Services provides valuable insights for managing logs in Azure environments. With these practices in place, developers can build robust and resilient Node.js applications that meet the highest standards of stability and reliability.