A dive into the Node.js ‘Util’ module

I’ve been doing some general exploration of core Node.js modules lately and recently took a look at the util module. I had seen it used here and there in the past but never really explored it further. What I found are several special use case but handy functions. Here’s a quick rundown of each, along with some explanation and examples. As usual, the official docs are great as well.

The docs note that util is maintained for internal use however the tools within it are generic enough that they could also benefit application and module developers. I’ve seen a variety of packages on NPM which address similar needs so it’s always interesting to see what can be done using core modules and avoiding excess dependencies.

Here’s the whole list of util functions (ignoring deprecated stuff):

  • util.debuglog: A straightforward debug logger that can be enabled/disabled with an environment variable.
  • util.deprecate: Wrap public functions with a simple deprecation notice.
  • util.format: String formatter (ala. sprintf, printf, etc. )
  • util.inherits: Update an object’s prototype to inherit from another's
  • util.inspect: More powerful logging of data with support for color, object depth traversal, and other options.

util.debuglog

Logging is one of those things that can easily get out of hand, sometimes accidentally committed, and can be annoying when you don’t want it. util.debuglog provides a simple logging function that is explicitly enabled using the NODE_DEBUG environment variable. Here’s a quick example…

const util = require('util');
const log = util.debuglog('API');
const myObject = {
url: 'http://eran.sh',
coffee: true,
address: {
address1: '123 Fake St.',
address2: 'Gainesville, FL',
},
tags: ['red', 'banana', 'golf']
};
log(myObject);

Before logging anything, util.debuglog is called and given a tag corresponding to the module within which it will be used. It then returns a function which can be called to log anything within your application.

By default, nothing will be logged above as util.debuglog is silent by default. Only when the above script is run with NODE_DEBUG=API node app.js will we actually see the output in the console. This allows you to leave helpful debug logging in your code without cluttering the console or inadvertently, adversely affecting performance.

Once the environment variable is set, the logging looks something like this:

In addition to the module name, the process ID is also shown. This can be helpful when dealing with multiple processes.

If you feel like util.debuglog is too limited, there are a variety of more elaborate logging solutions available. I’ve had good results with winston but there are many others out there as well.

More on util.debuglog here.

util.deprecate

When building a application or tool that other developers rely on, deprecation is a necessary practice which allows you, the maintainer, to continue evolving your tool’s API without also inadvertently removing something without warning that others depend on. util.deprecate is about as simple as it gets. Let’s say you’ve got a package on NPM that exposes a function for developers to use:

function capitalizeAndJoin(inputArray = []) {
return inputArray
.map(s => s.toString().toUpperCase())
.join(',')
}
module.exports = capitalizeAndJoin;

Here’s our developer’s code, using this module:

const capitalizeAndJoin = require(‘./capitalize-and-join’);
const demoArray = [‘hello’, ‘test’, ‘wee’, 8];
console.log(capitalizeAndJoin(demoArray))
// Outputs:
// HELLO,TEST,WEE,8

If we have decided to deprecate the capitalizeAndJoin function, we can do so easily with util.deprecate.

const util = require('util');
// ...
module.exports = util.deprecate(
capitalizeAndJoin,
'This function has been deprecated. Use something else instead.'
);

By wrapping our function with util.deprecate and providing a warning message, developers will receive a clear indication of when function may be nearing removal. Here’s what that looks like:

The program still runs as normal however the DeprecationWarning is displayed.

More on util.deprecate here.

util.format

If you’re familiar with PHP or other languages that include string formatting utilities, the lack of a printf function in JavaScript may not sit well with you. Fortunately, Node.js provides this functionality via util.format for easy use. The example may very well speak for itself:

const util = require('util');
const name = 'Eran';
const daysLeft = 4;
const message = util.format(
'Hello, %s! You have %d days remaining on your subscription.',
name,
daysLeft
);
console.log(message);
// Outputs:
// Hello, Eran! You have 4 days remaining on your subscription.

util.format accepts a format string which can include tokens such as %s, %d, and a few others. This allows you to write out a string without dealing with variable interpolation directly. This means that the string could be stored separately or pulled from a localized group of messages, keeping your variables separate.

More on util.format here.

util.inherits

In a world before ES2015 classes (love em or hate em), inheritance was something that had to be done somewhat manually in JavaScript. While it wasn’t required for everything, it was a pattern occasionally encouraged in Node.js. When writing an event emitter, for example, it was necessary to import the Node.js EventEmitter and then inherit from it for your own use. This would typically look like:

const util = require('util');
const EventEmitter = require('events');

function MyStream() {
EventEmitter.call(this);
}

util.inherits(MyStream, EventEmitter);

Now that JavaScript supports inheritances in a traditional OOP class sense, the encouraged manner to inherit from a superclass is to use extends without the need for util.inherits

const EventEmitter = require('events');

class MyStream extends EventEmitter {
constructor() {
super();
}
}

More on util.inherits here.

util.inspect

We end on yet another debugging tool, util.inspect is similar to using console.log for logging data structures. It returns a formatted string representation for easy human consumption. It’s not unlike the output you get when passing an object into console.log however it affords you more control. Check out the examples below…

const util = require('util');
const myObject = {
url: 'http://eran.sh',
coffee: true,
address: {
address1: '123 Fake St.',
address2: 'Gainesville, FL',
},
tags: ['red', 'banana', 'golf']
};
console.log(util.inspect(myObject))

The default behavior will give you something like this:

Inspect accepts a second parameter, a config object, which allows you to tailor the output. Some interesting items include…

const inspectOpts = {
showHidden: true, // Display non-enumerable properties
depth: 1, // How many nested properties deep should read
colors: true, // Display output in color
breakLength: 1 // # of items to be listed on a single line
};

Note: More options are available, check the docs for the full list.

The same object with the config options above applied.

The ability to control the depth of the data returned can be extremely helpful to prevent over-logging and making it hard to find the data you’re looking for which makes util.inspect a great tool to have at your disposal.

More on util.inspect here.

A unified logger for the lazy…

Just to bring some of these debug concepts to a logical conclusion, let’s look at an example in which we can use util.debuglog and util.inspect to make a simple, reusable, module for use in our application.

simple-logger.js

const { debuglog, inspect } = require('util');
const defaultOptions = {
depth: 1, // How many nested properties deep should read
colors: true, // Display output in color
breakLength: 20 // # of items to be listed on a single line
};
function getLogger(tagName = 'APP', overrideOptions = {}) {
const logger = debuglog(tagName);
const inspectOptions = Object.assign(
{},
defaultOptions,
overrideOptions
);
return debugData => logger(inspect(debugData, inspectOptions))
}
module.exports = getLogger;

This module can now be imported in our project like so…

const log = require('./simple-logger')();
const myObject = {
// ...
};
log(myObject);

Now we can categorize the util.debuglog tag when requiring the module or leave it blank to allow it to default to 'APP'. We’ve got some default config setup for util.inspect as well to make for pretty output.

Using an approach like this allows you to safely leave logging in place and only draw from it when needed by setting the NODE_DEBUG variable to 'APP'.

Thanks for reading!

That’s it for a quick rundown of the util module. If you’re interested in learning more about what can be done with the core modules of Node.js, I definitely recommend perusing the API documentation. Not only are the docs well written and maintained but it’s pretty much guaranteed that you’ll learn something new each time you dive in. Totally worth it!