Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions doc/api/util.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,42 @@ let debuglog = util.debuglog('internals', (debug) => {
});
```

### `debuglog().enabled`
<!-- YAML
added: REPLACEME
-->

* {boolean}

The `util.debuglog().enabled` getter is used to create a test that can be used
in conditionals based on the existence of the `NODE_DEBUG` environment variable.
If the `section` name appears within the value of that environment variable,
then the returned value will be `true`. If not, then the returned value will be
`false`.

```js
const util = require('util');
const enabled = util.debuglog('foo').enabled;
if (enabled) {
console.log('hello from foo [%d]', 123);
}
```

If this program is run with `NODE_DEBUG=foo` in the environment, then it will
output something like:

```console
hello from foo [123]
```

## `util.debug(section)`
<!-- YAML
added: REPLACEME
-->

Alias for `util.debuglog`. Usage allows for readability of that doesn't imply
logging when only using `util.debuglog().enabled`.

## `util.deprecate(fn, msg[, code])`
<!-- YAML
added: v0.8.0
Expand Down
47 changes: 35 additions & 12 deletions lib/internal/util/debuglog.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,34 @@
'use strict';

const {
FunctionPrototypeBind,
ObjectCreate,
ObjectDefineProperty,
RegExp,
RegExpPrototypeTest,
StringPrototypeToUpperCase
} = primordials;

const { inspect, format, formatWithOptions } = require('internal/util/inspect');

// `debugs` is deliberately initialized to undefined so any call to
// debuglog() before initializeDebugEnv() is called will throw.
let debugs;
let debugImpls;

let debugEnvRegex = /^$/;
let testEnabled;

// `debugEnv` is initial value of process.env.NODE_DEBUG
function initializeDebugEnv(debugEnv) {
debugs = {};
debugImpls = ObjectCreate(null);
if (debugEnv) {
debugEnv = debugEnv.replace(/[|\\{}()[\]^$+?.]/g, '\\$&')
.replace(/\*/g, '.*')
.replace(/,/g, '$|^')
.toUpperCase();
debugEnvRegex = new RegExp(`^${debugEnv}$`, 'i');
}
testEnabled = FunctionPrototypeBind(RegExpPrototypeTest, null, debugEnvRegex);
}

// Emits warning when user sets
Expand All @@ -37,41 +44,57 @@ function emitWarningIfNeeded(set) {

function noop() {}

function debuglogImpl(set) {
set = set.toUpperCase();
if (debugs[set] === undefined) {
if (debugEnvRegex.test(set)) {
function debuglogImpl(enabled, set) {
if (debugImpls[set] === undefined) {
if (enabled) {
const pid = process.pid;
emitWarningIfNeeded(set);
debugs[set] = function debug(...args) {
debugImpls[set] = function debug(...args) {
const colors = process.stderr.hasColors && process.stderr.hasColors();
const msg = formatWithOptions({ colors }, ...args);
const coloredPID = inspect(pid, { colors });
process.stderr.write(format('%s %s: %s\n', set, coloredPID, msg));
};
} else {
debugs[set] = noop;
debugImpls[set] = noop;
}
}
return debugs[set];
return debugImpls[set];
}

// debuglogImpl depends on process.pid and process.env.NODE_DEBUG,
// so it needs to be called lazily in top scopes of internal modules
// that may be loaded before these run time states are allowed to
// be accessed.
function debuglog(set, cb) {
function init() {
set = StringPrototypeToUpperCase(set);
enabled = testEnabled(set);
}
let debug = (...args) => {
init();
// Only invokes debuglogImpl() when the debug function is
// called for the first time.
debug = debuglogImpl(set);
debug = debuglogImpl(enabled, set);
if (typeof cb === 'function')
cb(debug);
debug(...args);
};
return (...args) => {
debug(...args);
let enabled;
let test = () => {
init();
test = () => enabled;
return enabled;
};
const logger = (...args) => debug(...args);
ObjectDefineProperty(logger, 'enabled', {
get() {
return test();
},
configurable: true,
enumerable: true
});
return logger;
}

module.exports = {
Expand Down
1 change: 1 addition & 0 deletions lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ module.exports = {
_exceptionWithHostPort: exceptionWithHostPort,
_extend,
callbackify,
debug: debuglog,
debuglog,
deprecate,
format,
Expand Down
4 changes: 2 additions & 2 deletions test/sequential/test-util-debug.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ function parent() {

function test(environ, shouldWrite, section, forceColors = false) {
let expectErr = '';
const expectOut = 'ok\n';
const expectOut = shouldWrite ? 'enabled\n' : 'disabled\n';

const spawn = require('child_process').spawn;
const child = spawn(process.execPath, [__filename, 'child', section], {
Expand Down Expand Up @@ -123,5 +123,5 @@ function child(section) {
}));
debug('this', { is: 'a' }, /debugging/);
debug('num=%d str=%s obj=%j', 1, 'a', { foo: 'bar' });
console.log('ok');
console.log(debug.enabled ? 'enabled' : 'disabled');
}