Native logging for your NPM package

November 29, 2022

When working with nodejs, if you are coding a NPM package, you might want to keep your package light but with basic logging.

One use-case could be you want your package to have 0 dependency, but allow the package's users to enable logs only when they need it. By default, any console.* in the code will appear in the output.

There is a native nodejs way with the util package, and especially the debuglog function.

Let's have a look to a simple example:

// ./index.js
const log = require('node:util').debuglog('foo')
log('starting package!')

This won't show any output unless you run it with the correct flag:

NODE_DEBUG=foo node index.js.

When debugging, you can add the flag NODE_DEBUG=foo to your NPM scripts (such npm run dev) to see all outputs. Make sure to let your users know, in order for them to see the logs when using/debugging the package.

There is a wildcard mode as well to match all custom logging you could create:

// index.js - node v18.11.0
const util = require('node:util')
const log = {
  network: util.debuglog('foo-network'),
  verbose: util.debuglog('foo-verbose')
  // <-- extend it to improve your logging!
}

log.verbose('starting package!')

new Promise(resolve => setTimeout(resolve, 2000))
  .then(_ => log.network('network call done'))

With the following outputs:

~ ❯ NODE_DEBUG=foo* node index.js
FOO-VERBOSE 23347: starting package!
FOO-NETWORK 23347: network call done

~ ❯ NODE_DEBUG=foo-network node index.js
FOO-NETWORK 23400: network call done
~

1st time was run with the wildcard on. And the 2nd time, in an imaginary scenario where the user would only need the NETWORK logs.

It is a good practice to namespace the section you pass to debuglog with your package name to allow granular debuging.

Fun fact, if you run a nodejs script with all debug logs activated (NODE_DEBUG=*), you can get more information on your script:

~ ❯ NODE_DEBUG=* node index.js
MODULE 23530: looking for "/Users/francois/index.js" in ["/Users/francois/.node_modules","/Users/francois/.node_libraries","/usr/local/Cellar/node/18.11.0/lib/node"]
MODULE 23530: load "/Users/francois/index.js" for module "."
MODULE 23530: Module._load REQUEST node:util parent: .
MODULE 23530: load built-in module node:util
FOO-VERBOSE 23530: starting package!
TIMER 23530: no 2000 list was found in insert, creating a new one
TIMER 23530: process timer lists 2087
TIMER 23530: timeout callback 2000
TIMER 23530: 2000 list empty
FOO-NETWORK 23530: network call done

ℹ️ Tip 1: if you need more options like adding colors, have a look to the debug package.

ℹ️ Tips 2: util.debuglog has an alias with util.debug! 😎