From 815678e8162d444ab441514f0754d14e44b0b6e8 Mon Sep 17 00:00:00 2001 From: Kevin Van Lierde Date: Thu, 3 Nov 2022 22:36:25 +0100 Subject: [PATCH 1/4] Adds timeFormat property, deprecates hideDate and aligns behavior across browser and Node * Adds timeFormat option to debug instances, settable as DEBUG_TIME_FORMAT env var * Deprecates hideDate option, equated to timeFormat === 'none' * Allows timeFormat of choice: none, iso, diff, or localized (taking into account process.env.TZ) * Normalizes behavior across browser & Node: all timeFormats are available in both, but the defaults are kept as-is. * Deprecates createDebug.humanize and adds createDebug.withTimeFormat helper instead --- src/browser.js | 10 ++++++++-- src/common.js | 38 ++++++++++++++++++++++++++++++++++++ src/node.js | 52 ++++++++++++++++++++++++++++++++++++-------------- 3 files changed, 84 insertions(+), 16 deletions(-) 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'; } } From 28f7b6d53e31031ef41c2dd1ade843905e2b1b5e Mon Sep 17 00:00:00 2001 From: Kevin Van Lierde Date: Thu, 3 Nov 2022 22:36:52 +0100 Subject: [PATCH 2/4] Documents timeFormat property --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) 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. From eedb474f3549a22b32753c11c647487fbe19d18c Mon Sep 17 00:00:00 2001 From: Kevin Van Lierde Date: Thu, 3 Nov 2022 22:37:36 +0100 Subject: [PATCH 3/4] Adds tests for timeFormat property. Demonstrates how debug output can be tested against both browser & NodeJS envs --- test.js | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) 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'); From 3e02f727bc5a80aeea0c1acf8446c9e432975361 Mon Sep 17 00:00:00 2001 From: Kevin Van Lierde Date: Fri, 4 Nov 2022 00:46:59 +0100 Subject: [PATCH 4/4] Document browserify build as NPM script --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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"