diff --git a/README.md b/README.md
index e9c3e047..ea29773d 100644
--- a/README.md
+++ b/README.md
@@ -137,7 +137,7 @@ When actively developing an application it can be useful to see when the time sp
-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:
@@ -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. |
@@ -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.
diff --git a/package.json b/package.json
index 3bcdc242..9a76b7f7 100644
--- a/package.json
+++ b/package.json
@@ -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"
diff --git a/src/browser.js b/src/browser.js
index cd0fc35d..896ae086 100644
--- a/src/browser.js
+++ b/src/browser.js
@@ -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;
@@ -137,6 +138,10 @@ function useColors() {
(typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/));
}
+function timeFormat() {
+ return 'diff';
+}
+
/**
* Colorize log arguments if enabled.
*
@@ -144,12 +149,13 @@ function useColors() {
*/
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;
diff --git a/src/common.js b/src/common.js
index e3291b20..3e4a6783 100644
--- a/src/common.js
+++ b/src/common.js
@@ -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 => {
@@ -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.
@@ -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.
diff --git a/src/node.js b/src/node.js
index 79bc085c..d9d7a411 100644
--- a/src/node.js
+++ b/src/node.js
@@ -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`.'
@@ -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);
}
@@ -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.
*
@@ -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() + ' ';
}
/**
@@ -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';
}
}
diff --git a/test.js b/test.js
index a1d6f633..c5889743 100644
--- a/test.js
+++ b/test.js
@@ -2,6 +2,7 @@
const assert = require('assert');
const debug = require('./src');
+const isBrowser = !!window;
describe('debug', () => {
it('passes a basic sanity check', () => {
@@ -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');