308 lines
7.4 KiB
JavaScript
308 lines
7.4 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
const path = require('path');
|
|
const util = require('util');
|
|
|
|
const chalk = require('chalk');
|
|
const dbug = require('dbug');
|
|
const stack = require('stack-trace');
|
|
const utc = require('utcstring');
|
|
|
|
const intel = require('./');
|
|
|
|
const ALIASES = [
|
|
'trace',
|
|
'debug',
|
|
'info',
|
|
'warn',
|
|
'error'
|
|
];
|
|
|
|
function copyProperties(source, target, props) {
|
|
props.forEach(function(prop) {
|
|
target[prop] = source[prop];
|
|
});
|
|
}
|
|
|
|
var ORIGINAL_METHODS = {};
|
|
const METHOD_NAMES = [
|
|
'trace',
|
|
'debug',
|
|
'dir',
|
|
'error',
|
|
'info',
|
|
'log',
|
|
'warn'
|
|
];
|
|
|
|
var ORIGINAL_STDERR;
|
|
|
|
function endsWith(str, suffix) {
|
|
return str.indexOf(suffix, str.length - suffix.length) !== -1;
|
|
}
|
|
|
|
var root;
|
|
var ignore;
|
|
var parentLogger;
|
|
var basename;
|
|
|
|
function getLoggerName(debugName) {
|
|
var trace = stack.get();
|
|
// walk up the stack until we find a function that isn't from this
|
|
// module. that's the calling module.
|
|
// ALSO: 'console.js' could be on the stack if console.trace() or
|
|
// something similar was called, cause we don't modify those, since
|
|
// they internally call console.log(), which is us!
|
|
var filename;
|
|
var debug = [];
|
|
if (debugName) {
|
|
debug = [
|
|
// debug@0.x
|
|
path.join('node_modules', 'debug', 'lib', 'debug.js'),
|
|
// debug@1.x
|
|
path.join('node_modules', 'debug', 'node.js'),
|
|
path.join('node_modules', 'debug', 'debug.js'),
|
|
// dbug@0.x
|
|
path.join('node_modules', 'dbug', 'lib', 'dbug.js'),
|
|
path.join('node_modules', 'dbug', 'index.js')
|
|
];
|
|
}
|
|
|
|
function skip(name) {
|
|
if (name === __filename || name === 'console.js') {
|
|
return true;
|
|
}
|
|
return debug.some(function(d) {
|
|
return endsWith(name, d);
|
|
});
|
|
}
|
|
|
|
for (var i = 0, len = trace.length; i < len; i++) {
|
|
filename = path.normalize(trace[i].getFileName());
|
|
if (!skip(filename)) {
|
|
break;
|
|
}
|
|
}
|
|
var topName = basename || path.basename(root);
|
|
topName = topName.replace(path.extname(topName), '');
|
|
|
|
var moduleName = path.join(topName, path.relative(root, filename));
|
|
moduleName = moduleName.replace(path.extname(moduleName), '');
|
|
moduleName = moduleName.replace(/[\\\/]/g, '.');
|
|
|
|
// lib is the defacto place to store js files, but removing lib looks
|
|
// better: connect.lib.session -> connect.session
|
|
moduleName = moduleName.replace(/\.lib\./g, '.');
|
|
|
|
// index.js filename shouldn't be used, since it's really the folder
|
|
// up. better: gryphon.index -> gryphon
|
|
moduleName = moduleName.replace(/\.index/g, '');
|
|
|
|
|
|
if (debugName) {
|
|
// clean up duplicated parts of the name
|
|
// ex: node_modules.intel.logger.intel.logger =>
|
|
// node_modules.intel.logger
|
|
if (!endsWith(moduleName, debugName)) {
|
|
moduleName += '.' + debugName;
|
|
}
|
|
}
|
|
|
|
if (parentLogger) {
|
|
moduleName = parentLogger + '.' + moduleName;
|
|
}
|
|
|
|
return moduleName;
|
|
}
|
|
|
|
function setRoot(r) {
|
|
root = r;
|
|
}
|
|
|
|
function setIgnore(i) {
|
|
if (typeof i === 'string') {
|
|
i = [i];
|
|
}
|
|
ignore = i;
|
|
}
|
|
|
|
const DEBUG_COLORED_RE = new RegExp([
|
|
'^',
|
|
' ', // starts with 2 spaces. yea really
|
|
'\\u001b\\[\\d{1,2};1m', // colored debug has colors
|
|
'(.+)', // logger name
|
|
'\\u001b\\[\\d?0m', // color end
|
|
'(.+)', // message
|
|
'\n?', // debug 2.0 addes a newline
|
|
'$'
|
|
].join(''));
|
|
|
|
const DBUG_LEVELS = ['debug', 'info', 'warn', 'error'];
|
|
|
|
|
|
function getLoggerLevel(name) {
|
|
var i = DBUG_LEVELS.length;
|
|
while (i--) {
|
|
var level = DBUG_LEVELS[i];
|
|
if (endsWith(name, '.' + level)) {
|
|
return level;
|
|
}
|
|
}
|
|
return 'debug';
|
|
}
|
|
|
|
function dbugName(name) {
|
|
if (!name) {
|
|
return;
|
|
}
|
|
if (name.indexOf('.') === -1) {
|
|
return name;
|
|
}
|
|
var level = getLoggerLevel(name.toLowerCase());
|
|
level = new RegExp('\\.' + level + '$', 'i');
|
|
return name.replace(level, '');
|
|
}
|
|
|
|
|
|
function parseDebug(args) {
|
|
// O_O
|
|
// Dear reader: I'm so sorry.
|
|
var str = String(args[0]);
|
|
|
|
// is it colored debug() ?
|
|
var match = str.match(DEBUG_COLORED_RE);
|
|
if (match) {
|
|
var logger = chalk.stripColor(match[1]).trim();
|
|
var msg = chalk.stripColor(match[2]).trim();
|
|
args[0] = msg; // overwrite the message portion
|
|
return logger.replace(/:/g, '.');
|
|
} else if (utc.has(str)) {
|
|
str = str.replace(utc.get(str), '').trim();
|
|
var logger = str.split(' ').shift();
|
|
var msg = str.replace(logger, '').trim();
|
|
args[0] = msg;
|
|
return logger.replace(/:/g, '.');
|
|
}
|
|
}
|
|
|
|
// cached loggers that hook into dbug.__log
|
|
// this way, we don't format a level message, and then parse it with
|
|
// regex, just to format a new message in intel
|
|
// ALSO: we only get a stacktrace once, and then save the name, so
|
|
// PERFORMANCE WIN!
|
|
var dbugLoggers = {};
|
|
function dbugHook(name, level, args) {
|
|
var logger = dbugLoggers[name];
|
|
if (!logger) {
|
|
logger = dbugLoggers[name] =
|
|
intel.getLogger(getLoggerName(name.replace(/:/g, '.')));
|
|
}
|
|
logger[level].apply(logger, args);
|
|
}
|
|
|
|
var isDebugging = false;
|
|
var __log = dbug.__log;
|
|
function setDebug(debug) {
|
|
if (debug === true) {
|
|
process.env.DEBUG = dbug.env = '*';
|
|
} else if (debug === false) {
|
|
process.env.DEBUG = dbug.env = '';
|
|
} else if (debug) {
|
|
if (process.env.DEBUG) {
|
|
process.env.DEBUG += ',' + debug;
|
|
dbug.env = process.env.DEBUG;
|
|
} else {
|
|
process.env.DEBUG = dbug.env = '' + debug;
|
|
}
|
|
}
|
|
isDebugging = !!debug;
|
|
dbug.__log = dbugHook;
|
|
}
|
|
|
|
function setLoggerBaseName(bn){
|
|
basename = bn;
|
|
}
|
|
|
|
function deliver(method, args) {
|
|
var debugged = isDebugging && parseDebug(args);
|
|
var name = getLoggerName(dbugName(debugged));
|
|
var i = ignore.length;
|
|
var logger = intel.getLogger(name);
|
|
name = logger._name;
|
|
while (i--) {
|
|
if (name.indexOf(ignore[i]) === 0) {
|
|
if (method === 'stderr') {
|
|
ORIGINAL_STDERR.call(process.stderr, args[0]);
|
|
} else {
|
|
ORIGINAL_METHODS[method].apply(console, args);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
if (debugged) {
|
|
method = getLoggerLevel(debugged);
|
|
}
|
|
var level = ALIASES.indexOf(method) !== -1 ? method : 'debug';
|
|
logger[level].apply(logger, args);
|
|
}
|
|
|
|
|
|
function overrideConsole(options) {
|
|
options = options || {};
|
|
setRoot(options.root || path.join(
|
|
stack.get(options.__trace || overrideConsole)[0].getFileName(),
|
|
'..'
|
|
));
|
|
setIgnore(options.ignore || []);
|
|
parentLogger = options.logger;
|
|
|
|
setDebug(options.debug);
|
|
|
|
setLoggerBaseName(options.basename);
|
|
if (!ORIGINAL_METHODS.log) {
|
|
copyProperties(console, ORIGINAL_METHODS, METHOD_NAMES);
|
|
}
|
|
|
|
if (!ORIGINAL_STDERR) {
|
|
ORIGINAL_STDERR = process.stderr.write;
|
|
}
|
|
|
|
ALIASES.forEach(function(method) {
|
|
console[method] = function alias(){
|
|
deliver(method, arguments);
|
|
};
|
|
});
|
|
|
|
console.log = function log() {
|
|
deliver('log', arguments);
|
|
};
|
|
|
|
console.dir = function dir(obj) {
|
|
deliver('dir', [util.inspect(obj)]);
|
|
};
|
|
|
|
process.stderr.write = function write(str) {
|
|
deliver('stderr', [str]);
|
|
return true;
|
|
};
|
|
}
|
|
|
|
function restoreConsole() {
|
|
for (var name in ORIGINAL_METHODS) {
|
|
if (ORIGINAL_METHODS.hasOwnProperty(name) && ORIGINAL_METHODS[name]) {
|
|
console[name] = ORIGINAL_METHODS[name];
|
|
}
|
|
}
|
|
copyProperties({}, ORIGINAL_METHODS, METHOD_NAMES);
|
|
dbug.__log = __log;
|
|
if (ORIGINAL_STDERR) {
|
|
process.stderr.write = ORIGINAL_STDERR;
|
|
ORIGINAL_STDERR = undefined;
|
|
}
|
|
}
|
|
|
|
module.exports = exports = overrideConsole;
|
|
exports.restore = restoreConsole;
|