Should you use a logging framework or console.log() in Node?

The console module is usually the first tool Node.js developers reach for when handling logging in an application. It's easy to use, native to the platform, and easy to read.

But then you might stumble upon a logging framework like Winston or Bunyan. The basic power of those tools might be attractive enough for you to consider switching.

But should you? How do you know which one you should use? Should basic console.log’s be avoided entirely and not even used?

What console does

While browsers implement console differently, in Node the console module will print to stdout and/or stderr. If you're using console.log() it will print to stdout and if you're using console.error() it will print to stderr.

Why does this matter?

Being able to write to stdout and stderr means that you can have Docker or logstash or whichever tool you're using easily pick up these logs. stdout and stderr on linux are pipeable, so this becomes easy.

By only using console, you're reducing a dependency (and all of it's dependencies) that you would have if you were using a logging framework. You don't even have to require/import the module like you do with some other native Node modules like fs.

Side note: above console refers to the global console, but it is possible to import console as a Class, which you can then instantiate to configure your own output streams rather than just stdout and stderr. I'm pointing this out just as a form of technical due diligence but it is not something you need to be concerned with for now as this is not the way console is usually used in Node. If you'd like to read more about the instantiation method however, you can check out the docs here.

Lastly, because it's common for front-end JavaScript developers to work on the Node parts of applications too, console has the same API methods as the console used by browsers, making it effortless to pick up.

Disadvantages of console

From the above, console seems like it should get the job done, so why would we even consider something else?

One of the biggest disadvantages is that you can't toggle logging on and off, not out of the box at least. You could wrap console and extend it to do this, but this is code you'll have to write, and likely code that will have to overwrite the built-in console functions.

Why would you want to turn off logs in the first place?

You might want to turn off logging if you're in a development environment vs a production environment. Or even if you're just testing locally on your machine or VM, if you've got a ton of logging for debug purposes or otherwise, that can really clutter up your console and you might want to just test with logging disabled for a bit.

Another disadvantage of console comes when you need to know log levels.

While it already has what appear to be log levels (see below), these are really just functions that route to stdout and stderr without providing true log levels.

console.log() --> writes to stdout
console.debug() --> writes to stdout
console.info() --> writes to stdout

console.error() --> writes to stderr
console.warn() --> writes to stderr

So in the Node console, you won't be able to tell these logs apart unless you prepend a string with the level to the logs.

Winston, a logging framework, will print out the level for you, like so:

{"message":"we hit an error","level":"error"}

With these things in mind, what does a logging framework give us that console doesn't?

To put it bluntly, a logging framework will help with what console doesn't. Popular Node logging frameworks like Winston and Bunyan allow for log levels, easy toggling logs on and off based on environment, and sometimes (in the case of Winston) support for custom log levels that you as a developer can define.

Logging frameworks will also (generally) support writing to more than just stdout/stderr. Winston calls these "transports" while Bunyan calls them "streams". For example, you can configure Winston or Bunyan to write to stdout, a file, and a database all at once.

Side note: this ability to write to multiple different outputs, however, is a feature I would recommend against using. The full explanation why is not within the scope of this post, and I will write a new one in the future with a more complete explanation, but for now know that the reason is to keep log routing separate from your application code.

Logging framework use cases still not clear enough?

Let's consider a real world example:

A requirement comes in that you need to log only errors in production, and add a custom level named "qa" to be logged in your QA/testing environment. Your DevOps team is sick of sifting through so many logs in production and only care about the errors. And your dev team wants to log results from a particular function call to a specific level that they can keep an eye on in while testing.

How can we accomplish this?

Only logging errors in production:

const winston = require('winston')
//process environment for PROD would be set to error, this is shortcut for demo
process.env.LOG_LEVEL = 'error'

const logger = winston.createLogger({
  level: process.env.LOG_LEVEL,
  transports: [
    new winston.transports.Console()
  ]
})

logger.info('this should not be logged because the level is not high enough')
logger.error('we hit an error, this will be logged')

Adding the custom "qa" level:

const winston = require('winston')

const customLevels = {
  levels: {
    qa: 6
  }
}

const customLogger = winston.createLogger({
  levels: customLevels.levels,
  transports: [
    new winston.transports.Console()
  ]
})

customLogger.qa('we hit the QA log')

Disadvantages of logging frameworks

I catalogued the shortcomings of console above, so it's only fair that I do the same for logging frameworks. And up until now, I've tried to be careful about particularly categorizing the disadvantages as "shortcomings" because it truly depends on your use case.

So, why might you not want to use a logging framework? When would vanilla console do?

The most immediate answer is that you're adding a dependency to your application, a dependency that might have lots of other dependencies and increase build/deploy time (albeit likely only very slightly), and one whose API is subject to change which could mean refactoring down the line.

They also might just be overkill for your scenario. If you don't have a use case for custom log levels, for writing to different targets (remember, I recommended against doing this), and you don't care if your logs are always logging, skip the framework.

Wrapping up

For parting recommendations, if you're just starting out (i.e. - building a new service or you don't have a logging framework pattern in place from other applications your team has built), use the console module.

Only reach for a logging framework if you have requirements that warrant it. If you're having trouble finding enough differences that would make you choose one over the other, go with console to start, and you can always switch later on.

Want to continue getting a better grasp on JavaScript and Node.js topics? Subscribe below to receive all my new posts and level up your skills!

Subscribe for all new posts!

No spam ever. Unsubscribe any time.