A dive into the Node.js ‘Util’ module

Eran Schoellhorn
Mar 28, 2017 · 7 min read
Image for post
Image for post

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

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:

Image for post
Image for post

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

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:

Image for post
Image for post

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

More on util.deprecate here.

util.format

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

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

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:

Image for post
Image for post

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.

Image for post
Image for post
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…

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'.

Image for post
Image for post

Thanks for reading!

ThreeFiveTwo

352 Inc Design and Engineering Blog

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store