For any application, an effective logging solution is very critical, confusing and time-consuming. A good logging solution builds your application more robust and easy to maintain the things on the production. In this tutorial, we will focus on the node.js logging solution and their best practices.

In the node.js, there are lots of logging library available but you need to choose one who is best for your application. For this tutorial, we are going to use the Winston NPM library for the node.js logging.

Things which you need to know before going further

Before proceding further, there are following things which you should know

Why Logging is important for the Application

As we already said that a good logging solution builds the application robust. Every node.js module or application needs logging to know real-time errors.

A good logging solution for the application enables you to

  • track all the data coming to the server from the clients
  • discover the errors easily on the production
  • track if all the services or APIs running successfully
  • and lots of other benefits

If you used javascript before then you know that in the javascript, all the logging done by the console.log(). This is the traditional way of doing the logging but you can not use this in your production. For the production, you must need a good logging library or framework like Winston.

What is Winston

Winston is a node.js logging library. It is one of the most popular logging library available for the node.js application.

According to winston npm library page, it is one of the most downloaded library for the node.js logging.

Winston supports lots of transport to log the data for the application. Transport is an essentially a storage device for your log. Each transport can be configured at different logging levels.

By default, winston gives with the following log levels:

  • error
  • warn
  • info
  • verbose
  • debug
  • custom error level

Winston also gives us the option to store the logs into the MongoDB or send an email to the developers on critical errors. We will learn all about this soon. keep reading this.

Install Winston

Before installing Winston, make sure you have to build a node.js project and know all the basic things about the node.js

You can install Winston in your node.js project by running the below command

npm install winston --save

Once you installed it, you can use it in your project like below

const winston = require('winston');

winston.log('info', 'log the data', {
  firstlog: 'codesquery'
})

Above code log below text into the console

info: log the data firstlog: 'codesquery'

In the above code, we used only info log level from the different others log levels. You can use other log levels in the same way into your code.

You can see how easy it is to use the winston library for your node.js logging.

But in the production, you can not use logging in such a way. For the enterprise level application, scalability is the first priority and all the code should be done according to that.

Use winston like a Professional

Till now , we learn how to use the winston library for the node.js logging.

But that is not enough for the production environment. In the production environment, we should keep all the logs according to their types and uses.

Again this the debatable topic and can vary person to person or company to company. But at last end goal is to keep the things less messy so that we can easily find our logs in the future.

Save Logs into a File

We can save all our logs into a file on a daily rotational basis. Winston gives the option so that we can save the logs at the desired location by using winston-daily-rotate-file npm library.

So let’s first install this npm library into our project by running the below command

npm install winston-daily-rotate-file --save

Once the library installed, we can write a code to save all the logs into the file.

Create a logger.js file in your project and start wrting the code into this.

Let’s understand all about the above code in step by step

Import required Modules and set the path

In the file, we first import all the required module for the node.js logging. Here we already install winston and winston-daily-rotate-file.

Next we define the desired path for the logs file where we will save all ours node.js logs.

After defining the path, we make sure that the log directory path exists in the project and if it does not exist then create the directory in the project.

const winston = require('winston');
const fs = require('fs');
require('winston-daily-rotate-file');

let logDirectory = 'your_logs_directory_path' //change this with your path

winston.setLevels(winston.config.npm.levels);
winston.addColors(winston.config.npm.colors);

//create directory if it is not present
if (!fs.existsSync(logDirectory)) {
  // Create the directory if it does not exist
  fs.mkdirSync(logDirectory);
}

Define Configuration Settings for the File Transport

Now, we will create a object options which contains the configuration settings for the file transport.

In this object, we are defining the log error level based on the environment (development/test/stage/production).

//winston options for various logging type
let options = {
  file: {
    level: process.env.ENV === 'development' ? 'debug' : 'info',
    filename: logDirectory + '/%DATE%-logsDemo.log',
    datePattern: 'YYYY-MM-DD',
    zippedArchive: true,
    timestamp: true,
    handleExceptions: true,
    humanReadableUnhandledException: true,
    prettyPrint: true,
    json: true,
    maxsize: 5242880, // 5MB
    colorize: true,
  }
};

When we define a logging level for a particular transport then anything at that level or higher will be logged into the file.

Winston uses npm following logging levels that are prioritized from highest(0) to lowest(5):

  1. error(0)
  2. warn(1)
  3. info(2)
  4. verbose(3)
  5. debug(4)
  6. silly(5)

Initiate the Winston

Till now, we configured everything for particular transport. Now we initiate a new winston logger with the file transport using the properties defined in the options file object.

module.exports.logger = new winston.Logger({
  transports: [
    new winston.transports.DailyRotateFile(options.file)
  ],
  exceptionHandlers: [
    new winston.transports.DailyRotateFile(options.file)
  ],
  exitOnError: false, // do not exit on handled exceptions
});

Integrating Winston Node.js Logging With Our Application

We created the winston file for our node.js logging for the application. Now to use this into our application we need to import this file and need to make express know about it.

Import the winston at the top of the file with other required modules

const logger = require('change_it_with_your_path').logger;

By using the below commands you can logs all the data into the file based on their error type

logger.error('Your error message'); //to log the errors in the application

logger.info('Your specific information');   // to log all the specific information in the application

Save logs into Database

Sometimes, we need to save the logs into our database instead of the file.

We can easily save the logs into our database by using winston another npm library winston-mongodb

To use the winstonmongodb library for our node.js logging, first, we have to install it into our application by running the below command

npm install winston-mongodb --save

After successfully installing the library, we can import this into our log file which we already created and create a new property in the options variable called database.

The database property of the options object contains the mongoDB configuration settings and looks like below

database: {
    db  : 'database_name',
    level: process.env.ENV === 'development' ? 'debug' : 'info',
  }

After adding the above property into the options object we need to initiate the Winston logger into the logger with the database setting like below

new winston.transports.MongoDB(options.database)

Send Email when Critical Errors logged

Like saving data into the database, We also want to inform the developer’s team about the critical errors on the production.

There are lots of other option also available by using which you can send an email to developers team.

But for this tutorial, we are using the winston library so we will use another winston helper library winston-mail which is used to send the emails.

The winston-mail library used the email.js behind it and provides the following options:

  • to: The address(es) you want to send to. [required]
  • from: The address you want to send from. (default: winston@[server-host-name])
  • host: SMTP server hostname (default: localhost)
  • port: SMTP port (default: 587 or 25)
  • username: User for server auth
  • password: Password for server auth
  • subject: Subject for email (default: winston: {{level}} {{msg}})
  • ssl: Use SSL (boolean or object { key, ca, cert })
  • tls: Boolean (if true, use starttls)
  • level: Level of messages that this transport should log.
  • unique: Boolean flag indicating whether to log only the declared level and none above.
  • silent: Boolean flag indicating whether to suppress output.
  • filter: Filter function with signature function({level, message, meta}). If specified, should return true for log messages that need to send.
  • html: Boolean flag indicating whether to send mail body as html attach.
  • timeout: Maximum number of milliseconds to wait for smtp responses (optional, defaults to emailjs defaults – 5000)
  • authentication: Preffered SMTP auth methods (optional, defaults to emailjs defaults – [‘PLAIN’, ‘CRAM-MD5’, ‘LOGIN’, ‘XOAUTH2’])
  • formatter: Custom mail body formatter with signature function({level, message, meta}). If specified, the return value will be used as mail body.

Like the DB transporter, we also need to define the mail transporter property into the options object and then initiate the Mail transporter.

The complete winston node.js logging file should looks like this:

const winston = require('winston');
const fs = require('fs');
require('winston-daily-rotate-file');
require('winston-mongodb');
require('winston-mail')

let logDirectory = 'your_logs_directory_path' //change this with your path

winston.setLevels(winston.config.npm.levels);
winston.addColors(winston.config.npm.colors);

//create directory if it is not present
if (!fs.existsSync(logDirectory)) {
  // Create the directory if it does not exist
  fs.mkdirSync(logDirectory);
}

//winston options for various logging type
let options = {
  file: {
    level: process.env.ENV === 'development' ? 'debug' : 'info',
    filename: logDirectory + '/%DATE%-logsDemo.log',
    datePattern: 'YYYY-MM-DD',
    zippedArchive: true,
    timestamp: true,
    handleExceptions: true,
    humanReadableUnhandledException: true,
    prettyPrint: true,
    json: true,
    maxsize: 5242880, // 5MB
    colorize: true,
  },
  database: {
    db  : 'database_name',
    level: process.env.ENV === 'development' ? 'debug' : 'info',
  },
  mail : {
    level: 'error',
    to: 'developers_team_email',
    from: 'sender_email',
    subject: 'An Error Occured On Server. Please Check IT ASAP',
    host: 'email_host',
    username: 'username',
    password: 'password'
  }
};

module.exports.logger = new winston.Logger({
  transports: [
    new winston.transports.DailyRotateFile(options.file),
    new winston.transports.MongoDB(options.database),
    new winston.transports.Mail(options.mail)
  ],
  exceptionHandlers: [
    new winston.transports.DailyRotateFile(options.file),
    new winston.transports.MongoDB(options.database),
    new winston.transports.Mail(options.mail)
  ],
  exitOnError: false, // do not exit on handled exceptions
});

Conclusion

In this tutorial, you learn how to use the Winston library for the node.js logging. You can do a lot more with this file to build a robust and scalable logging solution for your node.js applications.

We recommend that you should read all the docs of the library and then build the logging solution according to your project requirements.

If you have any suggestions or doubt then leave your thoughts or doubt in the comment section. We will try to reach to you as soon as possible with the effective results. And if you like this tutorial then share this post so that others can also learn about node.js logging.

Pin It