How to Log in NodeJS

Isaac Tony Feb 02, 2024
  1. Use Logging to Create and Store Logs in NodeJS
  2. Use console.log() to Track the Program Execution in NodeJS
  3. Conclusion
How to Log in NodeJS

Logging is the process of recording the application flow and behavior. Logging should be performed in production mode to a more persistent output stream.

Apart from aiding in application development, logging is an important aspect of debugging complex applications.

Furthermore, logging provides important information that can be used as an audit trail to monitor system behavior and diagnose problems faster.

Use Logging to Create and Store Logs in NodeJS

In a production environment, properly stored log files can also be used as a data source for analysis and ease the development process of new features.

Not all aspects of the application should be monitored. Deciding what information to log is an important part of the logging process.

Some types of data that cannot log include personal data such as user names, credit card numbers, and passwords.

In Node js and generally across most programming languages, various log levels can inform what kind of data to log. They include:

  • Warning
  • Error
  • Info
  • Debug
  • Verbose

Use console.log() to Track the Program Execution in NodeJS

We can use various methods to create and manage log files in Node js.

One of the most basic methods and perhaps the most used in the development stage is the console.log() function. Other similar methods include the console.err() and console.warn().

The console.log() and console.err() methods allow us to log messages to the standard output stdout and the standard error stderr.

In the example below, we have used the console.log() to track the program execution.

ages = [34, 33, 37, 29, 12, 10, 42, 44];

function check_ages(item) {
  if (item < 40) {
    console.info(`Age "${item}" is within the required range!`);
  } else {
    console.error(`Age "${item}" is out of the required range!`);
  }
}
for (const age of ages) {
  check_ages(age);
}

Output:

Age "34" is within the required range!
Age "33" is within the required range!
Age "37" is within the required range!
Age "29" is within the required range!
Age "12" is within the required range!
Age "10" is within the required range!
Age "42" is out of the required range!
Age "44" is out of the required range!

Now, while using console.log() to write log messages is super easy, using this method for applications deployed to production is not possible since, at this stage, you have no access to the console.

Furthermore, these methods do not indicate the severity or urgency of any error and, therefore, cannot be reliable when building more ultra-robust applications.

Suppose we wanted to store these logs in a certain file for further reference. To do that, we need to redirect the stdout and the stderr to a file with a .log extension that will store these logs, as shown here.

node index.js > log_info.log

Output:

Age "34" is within the required range!
Age "33" is within the required range!
Age "37" is within the required range!
Age "29" is within the required range!
Age "12" is within the required range!
Age "10" is within the required range!
Age "42" is out of the required range!
Age "44" is out of the required range!
Age "42" is out of the required range!
Age "44" is out of the required range!

Store the stderr and stdout logs in different files using the command shown here.

node index.js > stdout_main.log 2 > stderr_main.log

This will automatically create stdout_main.log and stderr_main.log files containing their respective logs, as shown below.

Age "34" is within the required range!
Age "33" is within the required range!
Age "37" is within the required range!
Age "29" is within the required range!
Age "12" is within the required range!
Age "10" is within the required range!
node : Age "42" is out of the required range!
At line:1 char:1
+ node index.js > stdout_main.log 2> stderr_main.log
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (Age "42" is out of the required range!:Strin
   g) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError

Age "44" is out of the required range!

While this method suits development stage needs in a production environment, we need a more flexible library that supports all logging levels and allows us to format and centralize logs.

With an average of 9,686,809 weekly downloads, Winston is a favorite simple universal logging library for Node js developers.

It is easy to configure while at the same time supporting different log formats, to provide different logging channels, also referred to as transports, and also allowing us to assign different priorities to our log levels.

Transports supported by Winston that we can use when we want to save or share our log files include: saving to files, email, databases, and the console.

To start using Winston, we first need to create and initialize a Node js application. We can install the Winston package using the npm package manager using the command below.

npm install Winston.

We can then require this package in our program to have access to its functionality, as shown below.

const winston = require('winston');

The most recommended way to use Winston is to create our logger using the Winston.createLogger() method.

Since we also need to specify configurations such as the transport, level, and format, we can create a separate configuration object within the logger.

const winston = require('winston');

const logger = winston.createLogger({

  transports: [new winston.transports.Console()

  ],

});

Once the configurations are in place, we can now log out messages. In this case, our transport is the console; therefore, our messages will be displayed in the console.

const winston = require('winston');

const logger = winston.createLogger({
  level: 'info',
  format: winston.format.json(),
  transports: [new winston.transports.Console()

  ],

});

logger.log({

  message: 'This is a message with Info log level !',

  level: 'info'
});

Output:

{"level":"info","message":"This is a message with Info log level !"}

Alternatively, we can create a separate configuration object and pass the object’s name to the logger, as shown below.

const winston = require('winston');

const logConfigs = {
  level: 'info',
  format: winston.format.json(),
  transports: [

    new winston.transports.Console()

  ],

}

const logger = winston.createLogger(logConfigs);

logger.log({

  message: 'This is a message with Info log level !',

  level: 'info'
});

We can also store our log files to a different transport. In this case, we want to store the logs in a file in an external folder.

To achieve this, we need to alter our transport under the configuration object instead of new Winston.transports.Console() have new Winston.transports.File and specify the location that dictates how the log files will be stored.

Here is an illustration.

const winston = require('winston');

const logger = winston.createLogger({
  level: 'info',
  format: winston.format.json(),
  'transports': [new winston.transports.File({filename: 'logs/files.log'})],

});

logger.log({

  message: 'This is a message with Info log level !',

  level: 'info'
});

Due to the flexible nature of the Winstone package under the configuration object, we can specify multiple transport forms simultaneously.

For instance, we can specify that all the logs are shown in the console. At the same time, those of another logging level, such as error level, are exclusively directed towards an external file or database.

const winston = require('winston');
const logConfigs = {
  transports: [
    new winston.transports.Console({level: 'warn'}),
    new winston.transports.File({
      level: 'error',
      // Create the log directory if it does not exist
      filename: 'logs/files.log'
    })
  ]
};
const logger = winston.createLogger(logConfigs);

logger.log({

  message: 'This is a message with warn log level !',

  level: 'warn'
});
logger.log({

  message: 'This is a message with error log level !',

  level: 'error'
});

Output:

{"level":"warn","message":"This is a message with warn log level !"}
{"level":"error","message":"This is a message with error log level !"}

Conclusion

The Winston package is not limited to transport mediums we have discussed above. We can also store our logs in a database a server or share them via email.

It is also worth noting that there are also other means of storing and managing logs in Node js, such as using Middleware software such as Bunyan, Log4js, or Morgan.

However, the Winston package stands out since it is easy to configure and flexible.

Author: Isaac Tony
Isaac Tony avatar Isaac Tony avatar

Isaac Tony is a professional software developer and technical writer fascinated by Tech and productivity. He helps large technical organizations communicate their message clearly through writing.

LinkedIn