Skip to content

Commit

Permalink
util: add debug and debuglog.enabled
Browse files Browse the repository at this point in the history
PR-URL: #33424
Reviewed-By: James M Snell <jasnell@gmail.com>
  • Loading branch information
bmeck committed Aug 10, 2020
1 parent 64acae2 commit bcfb176
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 14 deletions.
36 changes: 36 additions & 0 deletions doc/api/util.md
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
@@ -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
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
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');
}

0 comments on commit bcfb176

Please sign in to comment.