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

Custom output formatting #645

Open
wants to merge 8 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
189 changes: 164 additions & 25 deletions src/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,122 @@ function setup(env) {
*/
createDebug.formatters = {};

/**
* Map of formatting handling functions, for output formatting.
* m and _time are special hardcoded keys.
*/
createDebug.outputFormatters = {};

/**
* Map %m to applying formatters to arguments
*/

createDebug.outputFormatters.m = function (_, args) {
args[0] = createDebug.coerce(args[0]);

if (typeof args[0] !== 'string') {
// Anything else let's inspect with %O
/**
* Note: This only inspects the first argument,
* so if debug({foo: "bar"}, {foo: "bar"}) is passed
* only the first object will be colored by node's formatters.O
*/
args.unshift('%O');
}

// Apply any `formatters` transformations
let index = 0;
args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => {
// If we encounter an escaped % then don't increase the array index
if (match === '%%') {
return match;
}
index++;
const formatter = createDebug.formatters[format];
if (typeof formatter === 'function') {
const val = args[index];
match = formatter.call(this, val);

// Now we need to remove `args[index]` since it's inlined in the `format`
args.splice(index, 1);
index--;
}
return match;
});

return args;
};

/**
* Map %+ to humanize()'s defaults (1000ms diff => "1s")
*/

createDebug.outputFormatters['+'] = function () {
return '+' + createDebug.humanize(this.diff);
};

/**
* Map %d to returning milliseconds
*/

createDebug.outputFormatters.d = function () {
return '+' + this.diff + 'ms';
};

/**
* Map %n to outputting namespace prefix
*/

createDebug.outputFormatters.n = function () {
return this.namespace;
};

/**
* Map %_time to handling time...?
*/

createDebug.outputFormatters._time = function (format) {
// Browser doesn't have date
return new Date().toISOString();
};

/**
* Map of meta-formatters which are applied to outputFormatters
*/
createDebug.metaFormatters = {};

/**
* Map %J* to `JSON.stringify()`
*/

createDebug.outputFormatters.J = function (v) {
return JSON.stringify(v);
};

/**
* Map %c* to to `applyColor()`
*/

createDebug.outputFormatters.c = function (v) {
if (this.useColors) {
return this.applyColor(v);
} else {
return v;
}
};

/**
* Map %C* to to `applyColor(arg, bold = true)` (node)
*/

createDebug.outputFormatters.C = function (v) {
if (this.useColors) {
return this.applyColor(v, true);
} else {
return v;
}
};

/**
* Selects a color for a debug namespace
* @param {String} namespace The namespace string for the for the debug instance to be colored
Expand Down Expand Up @@ -80,44 +196,67 @@ function setup(env) {
self.curr = curr;
prevTime = curr;

args[0] = createDebug.coerce(args[0]);
// Apply relevant `outputFormatters` to `format`
const reg = /%([a-zA-Z+]+|[a-zA-Z]*?\{.+\})/;
let formattedArgs = [];
let res;
let outputFormat = self.format; // Make a copy of the format
while (res = outputFormat.match(reg)) {
let [matched, formatToken] = res;
let formatter;
let formatted;

// Split out the part before the matched format token
const split = outputFormat.slice(0, res.index);
outputFormat = outputFormat.slice(res.index + matched.length);

// And add it to the arguments
if (split.length > 0) {
formattedArgs.push(split);
}

if (typeof args[0] !== 'string') {
// Anything else let's inspect with %O
args.unshift('%O');
}
const metaFormatters = [];
// Extract metaformatters
while (formatToken.length > 1 && !formatToken.startsWith('{')) {
const metaFormatterToken = formatToken.slice(0, 1);
formatToken = formatToken.slice(1);
metaFormatters.push(createDebug.outputFormatters[metaFormatterToken]);
}

// Apply any `formatters` transformations
let index = 0;
args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => {
// If we encounter an escaped % then don't increase the array index
if (match === '%%') {
return match;
// Not really sure how to handle time at this point
if (formatToken.startsWith('{')) {
formatter = createDebug.outputFormatters._time;
} else {
formatter = createDebug.outputFormatters[formatToken];
}
index++;
const formatter = createDebug.formatters[format];
if (typeof formatter === 'function') {
const val = args[index];
match = formatter.call(self, val);

// Now we need to remove `args[index]` since it's inlined in the `format`
args.splice(index, 1);
index--;
formatted = formatter.call(self, formatToken, args);

// Apply metaFormatters
metaFormatters.forEach(metaFormatter => {
if (typeof metaFormatter === 'function') {
formatted = metaFormatter.call(self, formatted);
}
});

if (Array.isArray(formatted)) { // Intended to concatenate %m's args in the middle of the format
formattedArgs = formattedArgs.concat(formatted);
} else {
formattedArgs.push(formatted);
}
}
return match;
});

// Apply env-specific formatting (colors, etc.)
createDebug.formatArgs.call(self, args);
}

const logFn = self.log || createDebug.log;
logFn.apply(self, args);
logFn.apply(self, formattedArgs);
}

debug.namespace = namespace;
debug.enabled = createDebug.enabled(namespace);
debug.useColors = createDebug.useColors();
debug.format = createDebug.getFormat() || '%{H:M-Z}%n%m%+'; // ' %n%m%+'
debug.color = selectColor(namespace);
debug.applyColor = createDebug.applyColor.bind(debug);
debug.destroy = destroy;
debug.extend = extend;
// Debug.formatArgs = formatArgs;
Expand Down
53 changes: 33 additions & 20 deletions src/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ const util = require('util');

exports.init = init;
exports.log = log;
exports.formatArgs = formatArgs;
exports.applyColor = applyColor;
exports.save = save;
exports.load = load;
exports.useColors = useColors;
exports.getFormat = getFormat;

/**
* Colors.
Expand Down Expand Up @@ -137,7 +138,10 @@ exports.inspectOpts = Object.keys(process.env).filter(key => {
} else if (val === 'null') {
val = null;
} else {
val = Number(val);
const asNumber = Number(val);
if (!isNaN(asNumber)) {
val = asNumber;
}
}

obj[prop] = val;
Expand All @@ -155,31 +159,40 @@ function useColors() {
}

/**
* Adds ANSI color escape codes if enabled.
*
* @api public
* If DEBUG_FORMAT if specified, returns it.
* Otherwise, returns a format matching previous version's based on DEBUG_COLORS and DEBUG_HIDE_DATE
*/

function formatArgs(args) {
const {namespace: name, useColors} = this;

if (useColors) {
const c = this.color;
const colorCode = '\u001B[3' + (c < 8 ? c : '8;5;' + c);
const prefix = ` ${colorCode};1m${name} \u001B[0m`;
function getFormat() {
const useColors = 'colors' in exports.inspectOpts ?
Boolean(exports.inspectOpts.colors) :
tty.isatty(process.stderr.fd);

args[0] = prefix + args[0].split('\n').join('\n' + prefix);
args.push(colorCode + 'm+' + module.exports.humanize(this.diff) + '\u001B[0m');
if ('format' in exports.inspectOpts) {
return exports.inspectOpts.format;
} else {
args[0] = getDate() + name + ' ' + args[0];
if (useColors) {
return ' %Cn%m%c+'; // ' %n %m %+'
} else if (exports.inspectOpts.hideDate) {
return '%n%m'; // '%n %m'
} else {
return '%{%FT%T.%LZ%M-Z}%n%m'; // '%{%FT%T.%LZ%M-Z} %n %m'
}
}
}

function getDate() {
if (exports.inspectOpts.hideDate) {
return '';
}
return new Date().toISOString() + ' ';
/**
* Adds ANSI color escape codes if enabled.
*
* @api public
*/

function applyColor(str, bold = false) {
// I think doing this each time is a waste, colorCode could be stored in some variable?
const c = this.color;
const colorCode = '\u001B[3' + (c < 8 ? c : '8;5;' + c);

return colorCode + (bold ? ';1' : '') + 'm' + str + '\u001B[0m';
}

/**
Expand Down