Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exploratory PR for backwards-compatible changes & cross-env alignment: timeFormat #915

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ When actively developing an application it can be useful to see when the time sp

<img width="647" src="https://user-images.githubusercontent.com/71256/29091486-fa38524c-7c37-11e7-895f-e7ec8e1039b6.png">

When stdout is not a TTY, `Date#toISOString()` is used, making it more useful for logging the debug information as shown below:
When stdout is not a TTY, `Date#toISOString()` is used by default (when `DEBUG_TIME_FORMAT` is `iso`, see [Environment variables](#environment-variables)), making it more useful for logging the debug information as shown below:

<img width="647" src="https://user-images.githubusercontent.com/71256/29091956-6bd78372-7c39-11e7-8c55-c948396d6edd.png">

Expand Down Expand Up @@ -166,7 +166,8 @@ change the behavior of the debug logging:
| Name | Purpose |
|-----------|-------------------------------------------------|
| `DEBUG` | Enables/disables specific debugging namespaces. |
| `DEBUG_HIDE_DATE` | Hide date from debug output (non-TTY). |
| `DEBUG_HIDE_DATE` | *(deprecated) - use DEBUG_TIME_FORMAT=none instead* Hide date from debug output (non-TTY). |
| `DEBUG_TIME_FORMAT` | One of `diff` (TTY & browser default),`iso` (non-TTY default),`none`,`localized` |
| `DEBUG_COLORS`| Whether or not to use colors in the debug output. |
| `DEBUG_DEPTH` | Object inspection depth. |
| `DEBUG_SHOW_HIDDEN` | Shows hidden properties on inspected objects. |
Expand All @@ -178,6 +179,10 @@ See the Node.js documentation for
[`util.inspect()`](https://nodejs.org/api/util.html#util_util_inspect_object_options)
for the complete list.

__Note:__ In Node, if `DEBUG_TIME_FORMAT` is set to `localized`, you can control the timezone
by setting `process.env.TZ` to a valid [IANA timezone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones),
for example: `Europe/London`

## Formatters

Debug uses [printf-style](https://wikipedia.org/wiki/Printf_format_string) formatting.
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
"test": "npm run test:node && npm run test:browser && npm run lint",
"test:node": "istanbul cover _mocha -- test.js",
"test:browser": "karma start --single-run",
"test:coverage": "cat ./coverage/lcov.info | coveralls"
"test:coverage": "cat ./coverage/lcov.info | coveralls",
"build:browser": "browserify -s debug -t brfs -o dist/debug.js src/browser.js"
},
"dependencies": {
"ms": "2.1.2"
Expand Down
10 changes: 8 additions & 2 deletions src/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ exports.formatArgs = formatArgs;
exports.save = save;
exports.load = load;
exports.useColors = useColors;
exports.timeFormat = timeFormat;
exports.storage = localstorage();
exports.destroy = (() => {
let warned = false;
Expand Down Expand Up @@ -137,19 +138,24 @@ function useColors() {
(typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/));
}

function timeFormat() {
return 'diff';
}

/**
* Colorize log arguments if enabled.
*
* @api public
*/

function formatArgs(args) {
const wantsDiff = this.timeFormat === 'diff';
args[0] = (this.useColors ? '%c' : '') +
this.namespace +
(this.useColors ? ' %c' : ' ') +
args[0] +
(wantsDiff ? args[0] : module.exports.withTimeFormat(+new Date(), args[0], this.timeFormat)) +
(this.useColors ? '%c ' : ' ') +
'+' + module.exports.humanize(this.diff);
(wantsDiff ? module.exports.withTimeFormat(this.diff, '', this.timeFormat) : '');

if (!this.useColors) {
return;
Expand Down
38 changes: 38 additions & 0 deletions src/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ function setup(env) {
createDebug.disable = disable;
createDebug.enable = enable;
createDebug.enabled = enabled;
// no longer used, but kept accessible on createDebug for backwards-compatibility with debug <= 4.3.4
createDebug.humanize = require('ms');
createDebug.withTimeFormat = withTimeFormat;
createDebug.destroy = destroy;

Object.keys(env).forEach(key => {
Expand Down Expand Up @@ -115,6 +117,7 @@ function setup(env) {

debug.namespace = namespace;
debug.useColors = createDebug.useColors();
debug.timeFormat = createDebug.timeFormat();
debug.color = createDebug.selectColor(namespace);
debug.extend = extend;
debug.destroy = createDebug.destroy; // XXX Temporary. Will be removed in the next major release.
Expand Down Expand Up @@ -258,6 +261,41 @@ function setup(env) {
return val;
}

function getLocalizedDate(dt) {
const offset = dt.getTimezoneOffset();
const offsetSign = offset <= 0 ? '+' : '-';

// some timezones have sub-hour offsets
let offsetMins = offset % 60
let offsetHours = (offset - offsetMins) / 60;
dt.setHours(dt.getHours() - offsetHours);
dt.setMinutes(dt.getMinutes() - offsetMins);

const absMins = Math.abs(offsetMins);
const absHours = Math.abs(offsetHours);
offsetHours = offsetHours === 0 ? '00' : (absHours > 9 ? '' : '0') + absHours;
offsetMins = offsetMins === 0 ? '00' : (absMins > 9 ? '' : '0') + absMins;
// remove the 'Z' because it stands for UTC, returns in format like YYYY-MM-DD'T'HH:mm:ss.SSS +HH:mm
return dt.toISOString().slice(0, -1) + ' ' + offsetSign + offsetHours + ':' + offsetMins + ' ';
}

function withTimeFormat(date, str, format) {
switch (format) {
case 'iso':
return new Date(date).toISOString() + ' ' + str;
break;
case 'localized':
return getLocalizedDate(new Date(date)) + ' ' + str;
break;
case 'none':
return str;
break;
case 'diff':
default:
return str + '+' + createDebug.humanize(date);
}
}

/**
* XXX DO NOT USE. This is a temporary stub function.
* XXX It WILL be removed in the next major release.
Expand Down
52 changes: 38 additions & 14 deletions src/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ exports.formatArgs = formatArgs;
exports.save = save;
exports.load = load;
exports.useColors = useColors;
exports.timeFormat = timeFormat;
exports.destroy = util.deprecate(
() => {},
'Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.'
Expand Down Expand Up @@ -140,7 +141,7 @@ exports.inspectOpts = Object.keys(process.env).filter(key => {
val = false;
} else if (val === 'null') {
val = null;
} else {
} else if (Number(val) == val) {
val = Number(val);
}

Expand All @@ -158,6 +159,16 @@ function useColors() {
tty.isatty(process.stderr.fd);
}

/**
* Is stdout a TTY? Default timeFormat to 'diff', else 'iso'.
*/

function timeFormat() {
return 'timeFormat' in exports.inspectOpts ?
exports.inspectOpts.timeFormat :
(tty.isatty(process.stderr.fd) ? 'diff' : 'iso');
}

/**
* Adds ANSI color escape codes if enabled.
*
Expand All @@ -166,24 +177,27 @@ function useColors() {

function formatArgs(args) {
const {namespace: name, useColors} = this;
const timeFormat = this.timeFormat;
const withTimeFormat = module.exports.withTimeFormat;
const wantsDiff = timeFormat === 'diff';
const date = wantsDiff ? this.diff : +(new Date());

if (useColors) {
const c = this.color;
const colorCode = '\u001B[3' + (c < 8 ? c : '8;5;' + c);
const prefix = ` ${colorCode};1m${name} \u001B[0m`;
const prefix = `${colorCode};1m${name} \u001B[0m`;
const lines = args[0].split('\n')

args[0] = prefix + args[0].split('\n').join('\n' + prefix);
args.push(colorCode + 'm+' + module.exports.humanize(this.diff) + '\u001B[0m');
} else {
args[0] = getDate() + name + ' ' + args[0];
}
}
args[0] = (wantsDiff ? prefix : prefix + withTimeFormat(date, lines.length > 1 ? '\n' : '', timeFormat)) + lines
.map(line => (lines.length > 1 ? prefix : '') + line)
.join('\n');

function getDate() {
if (exports.inspectOpts.hideDate) {
return '';
if (wantsDiff) {
args.push(colorCode + 'm' + withTimeFormat(date, ' ', timeFormat) + '\u001B[0m');
}
} else {
args[0] = withTimeFormat(date, name + ' ' + args[0], timeFormat);
}
return new Date().toISOString() + ' ';
}

/**
Expand Down Expand Up @@ -230,10 +244,20 @@ function load() {

function init(debug) {
debug.inspectOpts = {};
const opts = exports.inspectOpts

const keys = Object.keys(exports.inspectOpts);
const keys = Object.keys(opts);
for (let i = 0; i < keys.length; i++) {
debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]];
debug.inspectOpts[keys[i]] = opts[keys[i]];
}

// set defaults for backwards compatibility
if (!opts.timeFormat) {
// equate opts.hideDate === true with opts.timeFormat === 'none'
if (opts.hideDate) {
opts.timeFormat = 'none';
}
opts.timeFormat = tty.isatty(process.stderr.fd) ? 'iso' : 'diff';
}
}

Expand Down
82 changes: 82 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

const assert = require('assert');
const debug = require('./src');
const isBrowser = !!window;

describe('debug', () => {
it('passes a basic sanity check', () => {
Expand Down Expand Up @@ -43,6 +44,87 @@ describe('debug', () => {
assert.deepStrictEqual(messages.length, 3);
});

it('exposes public members per instance', () => {
const log = debug('foo');
assert.deepStrictEqual(Object.keys(log), [
'namespace',
'useColors',
'timeFormat',
'color',
'extend',
'destroy',
'enabled'
].concat(isBrowser ? [] : ['inspectOpts']))
});

describe('timeFormat', () => {
const regex = isBrowser ? {
def: /%cfoo %chello%c \+0ms/,
iso: /%cfoo %c\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z hello%c \+0ms/,
localized: /%cfoo %c\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3} \+\d{2}:\d{2} hello%c /,
diff: /%cfoo %chello%c \+0ms,color: #99CC00,color: inherit,color: #99CC00/,
none: /%cfoo %chello%c /
} : {
def: /\x1B\[36;1mfoo \x1B\[0mhello\x1B\[36m \+0ms\x1B\[0m/,
iso: /\x1B\[36;1mfoo \x1B\[0m\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z hello/,
localized: /\x1B\[36;1mfoo \x1B\[0m\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3} \+\d{2}:\d{2} hello/,
diff: /\x1B\[36;1mfoo \x1B\[0mhello\x1B\[36m \+0ms\x1B\[0m/,
none: /\x1B\[36;1mfoo \x1B\[0mhello/
}

it('defaults to \'iso\' when non-TTY in Node, \'diff\' in browser', () => {
const log = debug('foo');
log.enabled = true;
let result = '';

log.log = str => result += str;
log('hello');
const match = regex.def.test(result);
console.log(result)
assert.strictEqual(match, true);
});

it('accepts value of \'localized\'', () => {
const log = debug('foo');
log.enabled = true;
log.timeFormat = 'localized';
let result = '';

log.log = str => result += str;
log('hello');
console.log(result)
const match = regex.localized.test(result);
assert.strictEqual(match, true);
});

it('accepts value of \'diff\'', () => {
const log = debug('foo');
log.enabled = true;
log.timeFormat = 'diff';
let result;

log.log = (...args) => {
result = args.join(',');
}
log('hello');
const match = regex.diff.test(result);
assert.strictEqual(match, true);
});

it('accepts value of \'none\'', () => {
const log = debug('foo');
log.enabled = true;
log.timeFormat = 'none';
let result = '';

log.log = str => result += str;
log('hello');
console.log(result)
const match = regex.none.test(result);
assert.strictEqual(match, true);
});
});

describe('extend namespace', () => {
it('should extend namespace', () => {
const log = debug('foo');
Expand Down