Nov 23, 2022 • 5 min read

5 Best Node.js Logging Libraries

author picture
Stanley Ulili
Developer

When an application crashes, you can read the stack trace to identify the problem.

But a stack trace only shows you the state of the application at the point of failure and not before it encountered the error.

This is where logging libraries come in.

They detail the application behavior before the point of failure and send them to your desired destination, such as a file or a monitoring tool.

There are many logging libraries available for Node.js, but not all of them are created equal.

In this article, we'll take a look at the top five logging libraries for Node.js:

We’ll analyze them based on their features and popularity to help you choose the right one for your needs. But first, let’s establish why you’d even want to use a logging library for Node.js.

Why Use Logging Libraries for Node.js?

Node.js ships with the console API, which contains methods that can be used for logging. But the logs tend to be difficult to read or filter since they are in a text format.

Most logging tools provide JSON support out of the box, and you can easily add dates, sort, or send logs to your desired destinations.

Consider the following example that uses the console API:

console.log("log"); console.info("custom message"); console.warn("Warning message"); console.trace("a stack trace");
Copy

When you run the program, you will see an output that looks like the following:

// Output: log custom message warning message Trace: a stack trace at Object.<anonymous>... ...
Copy

Now imagine if you had hundreds of these messages.

Not only would be difficult to know the message level or the date the messages were logged, but filtering and sorting the logs would also be a demanding task.

Now let’s compare the output above with a Node.js logging library’s output:

{"level":"error","message":"Error message","timestamp":"2022-09-20T11:39:33.953Z"} {"level":"warn","message":"Warning message","timestamp":"2022-09-20T11:39:33.957Z"} {"level":"info","message":"Info message","timestamp":"2022-09-20T11:39:33.957Z"}
Copy

Right off the bat, the output is in JSON format, which is easily machine-readable.

And this means you can send your logs to a variety of destinations. For example, you can send it to Highlight.

Try Highlight Today

Get the visibility you need

The level field tells you the level each message belongs to at a glance. Most libraries support the following levels(ordered from the most severe to the least severe):

  • fatal: indicates a serious problem that can stop the application from running
  • error: used to indicate that a required task failed
  • warn: indicates something that may cause a problem
  • info: used for messages that confirm that the application is behaving as it should
  • debug: used for messages that help when diagnosing an issue

Another interesting option in the output is the timestamp property, which tells you the date the message was logged. Since the logs are in JSON format, programs can use the date to easily filter or sort the messages.

On top of that, the logging tools also allow you to specify the destination you want to save your logs. You can even configure them to send logs to a specific file, database, or even cloud tools.

Now that you know why you should be using a logging library, we’ll go over the five best Node.js logging libraries.

5 Best Node.js Logging Libraries

#1 Winston

Winston is a popular, feature-rich, and flexible logging library for Node.js. The default format it uses is JSON, but it can be configured to send logs in multiple storage devices called transports.

Winston currently has 7 million weekly downloads on npm and 19 thousand stars on GitHub, making it the most-starred logging library in Node.js.

Here are some of Winston’s features:

  • JSON format: it creates JSON logs, which are easier to parse, sort, or filter
  • Multiple transports: Winston can send logs to files, databases, the console, or tools like AWS CloudWatch or Graylog
  • Supports child loggers
  • Allows you to query logs
  • Supports Node.js streams
  • Supports creating custom levels

How to Use Winston

To install Winston, enter the following command in your terminal:

npm install winston

Once you’ve installed it, create a winston_demo.js file and add the following code:

const winston = require("winston"); const logger = winston.createLogger({ transports: [new winston.transports.Console()], }); logger.error("Error message"); logger.warn("Warning message"); logger.info("Info message"); logger.verbose("Verbose message"); logger.debug("Debug message"); logger.silly("Silly message");
Copy

To create a Winston logger, you invoke Winston’s createLogger() method with a configuration object as its argument. You then assign the object a transports property with an instance of Console() so that Winston should send all logs to the console. Finally, you call the appropriate level method for each message you want to log.

Winston has seven levels (ordered from the most to least important): error, warn, info, verbose, debug, and silly.

When you run the program:

node winston_demo.js

It yields output that looks like the following:

// Output: {"level":"error","message":"Error message"} {"level":"warn","message":"Warning message"} {"level":"info","message":"Info message"}
Copy

Winston logs only messages in levels from info to error. To see all messages, you have to assign a level property to the config object:

... const logger = winston.createLogger({ level: "silly", transports: [new winston.transports.Console()], }) ...
Copy

Currently, the code in our winston_demo.js file prints JSON logs without dates. To add dates, you can use the format option in the config object:

const winston = require("winston"); const logger = winston.createLogger({ level: "info", format: winston.format.combine( winston.format.timestamp(), // adds a timestamp property winston.format.json() ), transports: [new winston.transports.Console()], }); logger.error("Error message"); logger.warn("Warning message"); logger.info("Info message"); // Output: // // {"level":"error","message":"Error message","timestamp":"2022-09-21T09:00:00.426Z"} // {"level":"warn","message":"Warning message","timestamp":"2022-09-21T09:00:00.428Z"} // {"level":"info","message":"Info message","timestamp":"2022-09-21T09:00:00.429Z"}
Copy

Winston also allows you to specify the file you want to send the logs to, as well as the level:

const winston = require("winston"); const logger = winston.createLogger({ level: "info", format: winston.format.combine( winston.format.timestamp(), winston.format.json() ), transports: [ new winston.transports.File({ filename: "error.log", level: "warn" }), new winston.transports.File({ filename: "app.log" }), ], }); logger.error("Error message"); logger.warn("Warning message"); logger.info("Info message");
Copy

After running the file, you will find that two files have been created: error.log and app.log.

When you open error.log, you will see error and warning logs only:

{"level":"error","message":"Error message","timestamp":"2022-09-21T09:01:45.843Z"} {"level":"warn","message":"Warning message","timestamp":"2022-09-21T09:01:45.846Z"}
Copy

When you inspect app.log, you will see the following:

{"level":"error","message":"Error message","timestamp":"2022-09-21T09:01:45.843Z"} {"level":"warn","message":"Warning message","timestamp":"2022-09-21T09:01:45.846Z"} {"level":"info","message":"Info message","timestamp":"2022-09-21T09:01:45.846Z"}
Copy

You now have a general idea of how Winston works. But there’s so much more to unpack in the Winston documentation on Github.

#2 Pino

Pino is another popular JSON logging tool. It is more lightweight and claims to be 5x faster than other logging libraries. At the time of writing, it has 3 million weekly downloads on npm and has 10K Github stars.

The following are some of the features :

How to Use Pino

First, install Pino with the following command in a new directory:

npm install pino

Next, create a logger.js file to initialize the logger:

const pino = require("pino"); module.exports = pino({});
Copy

To initialize the logger, you call the pino() function with a configuration object. You can leave the object empty for now.

Next, create a pino_demo.js file with the following contents:

const logger = require("./logger"); logger.fatal("Fatal message"); logger.error("Error message"); logger.warn("Warn message"); logger.info("Info message"); logger.debug("Debug message"); logger.trace("Trace message"); // Output: // {"level":60,"time":1663751177889,"pid":39927,"hostname":"node","msg":"Fatal message"} // {"level":50,"time":1663751177890,"pid":39927,"hostname":"node","msg":"Error message"} // {"level":40,"time":1663751177890,"pid":39927,"hostname":"node","msg":"Warn message"} // {"level":30,"time":1663751177890,"pid":39927,"hostname":"node","msg":"Info message"}
Copy

In the output, Pino prints messages from only fatal to info. To see all levels, you can specify the level property in the config object:

... module.exports = pino( { level: "info", })
Copy

If you want to format and colorize the output during development, you can install the pino-pretty module:

npm install pino-pretty --save-dev

To use it, pipe the commands as follows:

node pino_demo.js | npx pino-pretty

As mentioned, you can configure Pino to send logs to other transports (storage devices). To do that, you can use Node.js streams with the help of the fs module.

Open logger.js and add the following:

const pino = require("pino"); const fs = require("fs"); const streams = [ { level: "info", // log INFO and above stream: fs.createWriteStream("./app.log", { flags: "a" }), }, { level: "error", // log INFO and above stream: fs.createWriteStream("./error.log", { flags: "a" }), }, ]; module.exports = pino( { level: "info", }, pino.multistream(streams) );
Copy

When you save and run the file, you will see that two files in your directory have been created that contain the log contents.

Now that you know the basics of using Pino, see Pino’s documentation to learn more.

#3 Bunyan

Bunyan is another invaluable tool that can be used for logging. It is a strong advocate of JSON logs and currently has 1.7 million weekly downloads on npm and 6.9K Github stars.

Bunyan provides a simple API alongside many useful features:

  • Transports: using Nodejs streams, you can configure bunyan to send logs to the console, files, streams, etc.
  • Pretty printing: bunyan provides a CLI for prettifying and filtering logs
  • Supports multiple runtime environments, such as [Node.js], Browserify, Webpack, and Nw.js
  • Child loggers
  • Node.js streams support

How to Use Bunyan

In a new directory, install Bunyan via npm:

npm install bunyan

Next, create a file bunyan_demo.js with the following:

const bunyan = require("bunyan"); const log = bunyan.createLogger({ name: "demo_app" }); log.fatal("Fatal message"); log.error("Error message"); log.warn("Warn message"); log.info("Info message"); log.debug("Debug message"); log.trace("Trace message");
Copy

In the preceding code block, you invoke Bunyan’s createLogger() method with a configuration object as an argument. You then assign the application name using the name property.

Now, run the file:

node bunyan_demo.js

You will see output that looks like the following:

// Output: {"name":"demo_app","hostname":"node","pid":40239,"level":60,"msg":"Fatal message","time":"2022-09-21T09:22:11.541Z","v":0} {"name":"demo_app","hostname":"node","pid":40239,"level":50,"msg":"Error message","time":"2022-09-21T09:22:11.542Z","v":0} {"name":"demo_app","hostname":"node","pid":40239,"level":40,"msg":"Warn message","time":"2022-09-21T09:22:11.542Z","v":0} {"name":"demo_app","hostname":"node","pid":40239,"level":30,"msg":"Info message","time":"2022-09-21T09:22:11.542Z","v":0}
Copy

Similar to Wiston and Pino, Bunyan only logs from info level and goes up to the fatal level. You can use the level property to change this behavior.

You can also make use of the Bunyan CLI to tidy the output:

node bunyan_demo.js | ./node_modules/.bin/bunyan

To configure Bunyan to send logs to files, you can use the streams property, which accepts an array of transports:

const bunyan = require("bunyan"); const log = bunyan.createLogger({ name: "demo_app", streams: [ { level: "info", // log INFO and above path: "./app.log", }, { level: "error", // log ERROR and above path: "./error.log", }, ], }); log.fatal("Fatal message"); log.error("Error message"); log.warn("Warn message"); log.info("Info message");
Copy

The errors are sent to the error.log file, and the rest are sent to the app.log.

To continue exploring the Bunyan Node.js logging library, visit their documentation page.

#4 Loglevel

Loglevel is a lightweight logging library, and currently has 9 million weekly downloads on npm and 2.3K stars on Github.

But it lacks a lot of features available in the logging tools we have looked at so far, notably native JSON support.

Depending on your use case, it has some features worth considering:

How to Use Loglevel

First, install loglevel in a new directory:

npm install loglevel

Create a loglevel_demo.js file with the following contents:

log = require("loglevel"); log.setLevel("info"); log.info("Info message"); log.warn("Warn message"); log.error("Error message");
Copy

When you run the file, you will see an output resembling the following:

Info message Warn message Error message
Copy

As you can see, it is much easier to set up and use in your project. However, the logs are not in JSON format, which can make filtering difficult.

If you want to learn more about Loglevel, visit the documentation page on Github.

#5 Npmlog

Last on our list is npmlog, a logging tool used by the npm project. It currently has 23 million weekly downloads and 390 stars on GitHub.

Npmlog is simple and lightweight, making it a great option for beginners.

Here are some of the features:

  • Colorized output in the terminal
  • Ability to define custom levels

How to Use Npmlog

First, install the npmlog library:

npm install npmlog

Next, create a npmlog_demo.js file:

const log = require("npmlog"); log.silly("Silly message"); log.verbose("Verbose Message"); log.info("Prefix", "Info Message"); log.http("HTTP message"); log.warn("Warning message"); const errorCode = 500; log.error("Error", "Message: %d", errorCode);
Copy

Running the file yields output that looks like this:

From the output, only levels from info to error are logged. Also, note that the output is colorized allowing you to differentiate the messages. However, since Npmlog doesn’t use the JSON format, it can be a huge drawback, especially when you want to sort or filter them.

If you want npmlog to show all the messages, you can use the level property:

const log = require("npmlog"); log.level = "silly" ...
Copy

You can also define your custom levels:

const log = require("npmlog"); // define a level log.addLevel("success", 2000, { fg: "green" }); // using your custom level log.success("Prefix", "success message"); // success Prefix success message
Copy

To save logs in a file, you can use the npmlog-file library. To use it, install npmlog-file in your directory:

npm install npmlog-file

Next, add the following code to your file like so:

const log = require("npmlog"); // import npmlogfile const logfile = require("npmlog-file"); const errorCode = 500; log.info("Prefix", "Info Message"); log.http("HTTP message"); log.warn("Warning message"); log.error("Error", "Message: %d", errorCode); // write logs to the "app.log" file logfile.write(log, "app.log");
Copy

When running the file, you will find the app.log created with the log contents.

To explore npmlog, visit the npmlog documentation.

Streamline Your Debugging Process With Node.Js Logging Libraries

If you're looking for a fast and efficient way to debug your Node.js applications, consider using one of the libraries we discussed in this article.

Each library has its own unique set of features that can help streamline your debugging process. But we do have to recommend Winston, Pino, or Bunyan due to their popularity and ease of use.

These logging libraries have proved to be invaluable when it comes to debugging Node.js applications in development and production environments.

And when paired with a monitoring tool like Highlight, they can provide you with even more insights into your Node.js applications.

Other articles you may like

The beauty of contract-first API design
8 Tips to Help You Maximize Chrome DevTools
What is Frontend Monitoring and What Tools Help You Do It?
Try Highlight Today

Get the visibility you need