有正确行号的 console.log 的正确包装器?

我现在正在开发一个应用程序,并放置一个全局 isDebug开关。我想包装 console.log更方便的使用。

//isDebug controls the entire site.
var isDebug = true;


//debug.js
function debug(msg, level){
var Global = this;
if(!(Global.isDebug && Global.console && Global.console.log)){
return;
}
level = level||'info';
Global.console.log(level + ': '+ msg);
}


//main.js
debug('Here is a msg.');

然后我在 Firefox 控制台中得到这个结果。

info: Here is a msg.                       debug.js (line 8)

如果我想用行号记录 debug()被调用的地方,比如 info: Here is a msg. main.js (line 2),那该怎么办?

103795 次浏览

如何得到 JavaScript 调用方函数的行号? 如何得到 JavaScript 调用方源 URL? Error对象有一个行号属性(在 FF 中):

var err = new Error();
Global.console.log(level + ': '+ msg + 'file: ' + err.fileName + ' line:' + err.lineNumber);

在 Webkit 浏览器中,err.stack是表示当前调用堆栈的字符串。它将显示当前行号和更多信息。

更新

为了得到正确的行号,你需要调用该行上的错误,比如:

var Log = Error;
Log.prototype.write = function () {
var args = Array.prototype.slice.call(arguments, 0),
suffix = this.lineNumber ? 'line: '  + this.lineNumber : 'stack: ' + this.stack;


console.log.apply(console, args.concat([suffix]));
};


var a = Log().write('monkey' + 1, 'test: ' + 2);


var b = Log().write('hello' + 3, 'test: ' + 4);

您可以将行号传递给调试方法,如下所示:

//main.js
debug('Here is a msg.', (new Error).lineNumber);

在这里,(new Error).lineNumber将给出 javascript代码中的当前行号。

如果您只是想控制是否使用了 debug 并且具有正确的行号,您可以这样做:

if(isDebug && window.console && console.log && console.warn && console.error){
window.debug = {
'log': window.console.log,
'warn': window.console.warn,
'error': window.console.error
};
}else{
window.debug = {
'log': function(){},
'warn': function(){},
'error': function(){}
};
}

当您需要访问调试时,可以这样做:

debug.log("log");
debug.warn("warn");
debug.error("error");

如果是 isDebug == true,控制台中显示的行号和文件名将是正确的,因为 debug.log等实际上是 console.log等的别名。

如果是 isDebug == false,则不显示调试消息,因为 debug.log等什么都不做(一个空函数)。

众所周知,包装函式会把行号和文件名搞混,所以最好不要使用包装函数。

//isDebug controls the entire site.
var isDebug = true;


//debug.js
function debug(msg, level){
var Global = this;
if(!(Global.isDebug && Global.console && Global.console.log)){
return;
}
level = level||'info';
return 'console.log(\'' + level + ': '+ JSON.stringify(msg) + '\')';
}


//main.js
eval(debug('Here is a msg.'));

这会给我 info: "Here is a msg." main.js(line:2)

但是需要额外的 eval,可惜。

我喜欢 @ Fredrik 的回答,所以我把它和 分离 Webkit 堆栈跟踪的另一个答案卷起来,然后和 @ PaulIrish 的保险箱,日志包装合并。“标准化”的 filename:line的“特殊对象”,使其脱颖而出,看起来几乎相同的 FF 和 Chrome。

小提琴测试: http://jsfiddle.net/drzaus/pWe6W/

_log = (function (undefined) {
var Log = Error; // does this do anything?  proper inheritance...?
Log.prototype.write = function (args) {
/// <summary>
/// Paulirish-like console.log wrapper.  Includes stack trace via @fredrik SO suggestion (see remarks for sources).
/// </summary>
/// <param name="args" type="Array">list of details to log, as provided by `arguments`</param>
/// <remarks>Includes line numbers by calling Error object -- see
/// * http://paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/
/// * https://stackoverflow.com/questions/13815640/a-proper-wrapper-for-console-log-with-correct-line-number
/// * https://stackoverflow.com/a/3806596/1037948
/// </remarks>


// via @fredrik SO trace suggestion; wrapping in special construct so it stands out
var suffix = {
"@": (this.lineNumber
? this.fileName + ':' + this.lineNumber + ":1" // add arbitrary column value for chrome linking
: extractLineNumberFromStack(this.stack)
)
};


args = args.concat([suffix]);
// via @paulirish console wrapper
if (console && console.log) {
if (console.log.apply) { console.log.apply(console, args); } else { console.log(args); } // nicer display in some browsers
}
};
var extractLineNumberFromStack = function (stack) {
/// <summary>
/// Get the line/filename detail from a Webkit stack trace.  See https://stackoverflow.com/a/3806596/1037948
/// </summary>
/// <param name="stack" type="String">the stack string</param>


if(!stack) return '?'; // fix undefined issue reported by @sigod


// correct line number according to how Log().write implemented
var line = stack.split('\n')[2];
// fix for various display text
line = (line.indexOf(' (') >= 0
? line.split(' (')[1].substring(0, line.length - 1)
: line.split('at ')[1]
);
return line;
};


return function (params) {
/// <summary>
/// Paulirish-like console.log wrapper
/// </summary>
/// <param name="params" type="[...]">list your logging parameters</param>


// only if explicitly true somewhere
if (typeof DEBUGMODE === typeof undefined || !DEBUGMODE) return;


// call handler extension which provides stack trace
Log().write(Array.prototype.slice.call(arguments, 0)); // turn into proper array
};//--  fn  returned


})();//--- _log

这在 node 中也可以使用,您可以使用以下方法进行测试:

// no debug mode
_log('this should not appear');


// turn it on
DEBUGMODE = true;


_log('you should', 'see this', {a:1, b:2, c:3});
console.log('--- regular log ---');
_log('you should', 'also see this', {a:4, b:8, c:16});


// turn it off
DEBUGMODE = false;


_log('disabled, should not appear');
console.log('--- regular log2 ---');

保持行号的一种方法是: https://gist.github.com/bgrins/5108712。它或多或少可以归结为:

if (Function.prototype.bind) {
window.log = Function.prototype.bind.call(console.log, console);
}
else {
window.log = function() {
Function.prototype.apply.call(console.log, console, arguments);
};
}

如果没有进行调试,可以用 isDebug包装它,并将 window.log设置为 function() { }

你可以保持行号 还有输出日志级别与一些聪明的使用 Function.prototype.bind:

function setDebug(isDebug) {
if (window.isDebug) {
window.debug = window.console.log.bind(window.console, '%s: %s');
} else {
window.debug = function() {};
}
}


setDebug(true);


// ...


debug('level', 'This is my message.'); // --> level: This is my message. (line X)

更进一步,您可以利用 console的错误/警告/信息区别,并且仍然有自定义级别

function setDebug(isDebug) {
if (isDebug) {
window.debug = {
log: window.console.log.bind(window.console, 'log: %s'),
error: window.console.error.bind(window.console, 'error: %s'),
info: window.console.info.bind(window.console, 'info: %s'),
warn: window.console.warn.bind(window.console, 'warn: %s')
};
} else {
var __no_op = function() {};


window.debug = {
log: __no_op,
error: __no_op,
warn: __no_op,
info: __no_op
}
}
}


setDebug(true);


// ...


debug.log('wat', 'Yay custom levels.'); // -> log: wat: Yay custom levels.    (line X)
debug.info('This is info.');            // -> info: This is info.        (line Y)
debug.error('Bad stuff happened.');     // -> error: Bad stuff happened. (line Z)

我发现这个问题的一些答案对我的需要来说有点太复杂了。这里有一个简单的解决方案,在 Coffeescript 呈现。它改编自布莱恩 · 格兰斯蒂德的 给你版本

它假定全局控制台对象。

# exposes a global 'log' function that preserves line numbering and formatting.
(() ->
methods = [
'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error',
'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log',
'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd',
'timeStamp', 'trace', 'warn']
noop = () ->
# stub undefined methods.
for m in methods  when  !console[m]
console[m] = noop


if Function.prototype.bind?
window.log = Function.prototype.bind.call(console.log, console);
else
window.log = () ->
Function.prototype.apply.call(console.log, console, arguments)
)()

来自 http://www.briangrinstead.com/blog/console-log-helper-function的代码:

// Full version of `log` that:
//  * Prevents errors on console methods when no console present.
//  * Exposes a global 'log' function that preserves line numbering and formatting.
(function () {
var method;
var noop = function () { };
var methods = [
'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error',
'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log',
'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd',
'timeStamp', 'trace', 'warn'
];
var length = methods.length;
var console = (window.console = window.console || {});


while (length--) {
method = methods[length];


// Only stub undefined methods.
if (!console[method]) {
console[method] = noop;
}
}




if (Function.prototype.bind) {
window.log = Function.prototype.bind.call(console.log, console);
}
else {
window.log = function() {
Function.prototype.apply.call(console.log, console, arguments);
};
}
})();


var a = {b:1};
var d = "test";
log(a, d);

这是一个古老的问题,所有提供的答案都过于庸俗,有 MAJOR 跨浏览器的问题,并且没有提供任何超级有用的东西。这个解决方案可以在每个浏览器中工作,并报告所有控制台数据。没有黑客需要和一行代码 看看 Codeen

var debug = console.log.bind(window.console)

创建这样的开关:

isDebug = true // toggle this to turn on / off for global controll


if (isDebug) var debug = console.log.bind(window.console)
else var debug = function(){}

然后按如下方式呼叫:

debug('This is happening.')

您甚至可以使用下面的开关来接管 console. log:

if (!isDebug) console.log = function(){}

如果你想做点有用的事的话。.您可以添加所有控制台方法,并将其包装在一个可重用的函数中,该函数不仅提供全局控制,而且还提供类级别:

var Debugger = function(gState, klass) {


this.debug = {}


if (gState && klass.isDebug) {
for (var m in console)
if (typeof console[m] == 'function')
this.debug[m] = console[m].bind(window.console, klass.toString()+": ")
}else{
for (var m in console)
if (typeof console[m] == 'function')
this.debug[m] = function(){}
}
return this.debug
}


isDebug = true //global debug state


debug = Debugger(isDebug, this)


debug.log('Hello log!')
debug.trace('Hello trace!')

现在你可以把它添加到你的类中:

var MyClass = function() {
this.isDebug = true //local state
this.debug = Debugger(isDebug, this)
this.debug.warn('It works in classses')
}

ChromeDevtools 可以让你通过 黑拳实现这一点。您可以创建 console. log 包装器,它可以产生副作用,调用其他函数,等等,同时仍然保留调用包装函式的行号。

只需将一个小的 console.log 包装器放到一个单独的文件中,例如。

(function() {
var consolelog = console.log
console.log = function() {
// you may do something with side effects here.
// log to a remote server, whatever you want. here
// for example we append the log message to the DOM
var p = document.createElement('p')
var args = Array.prototype.slice.apply(arguments)
p.innerText = JSON.stringify(args)
document.body.appendChild(p)


// call the original console.log function
consolelog.apply(console,arguments)
}
})()

将其命名为 log-blackbox.js

然后转到 ChromeDevtools 设置,找到“ Blackbox”部分,为你想要的 blackbox 文件名添加一个模式,在本例中是 log-blackbox。JS

我最近也在研究这个问题。需要一些非常直接的东西来控制日志记录,但也要保留行号。我的解决方案在代码中看起来并不优雅,但是提供了我所需要的东西。如果一个人足够小心的关闭和保留。

我在应用程序的开头添加了一个小包装:

window.log = {
log_level: 5,
d: function (level, cb) {
if (level < this.log_level) {
cb();
}
}
};

这样以后我就可以简单地做:

log.d(3, function(){console.log("file loaded: utils.js");});

我已经测试了 Firefox 和 crome,两个浏览器似乎都按预期显示了控制台日志。如果您像这样填充,那么您总是可以扩展‘ d’方法并向它传递其他参数,这样它就可以执行一些额外的日志记录。

我的方法还没有发现任何严重的缺点,除了日志代码中丑陋的一行。

堆栈跟踪解决方案 展示的行号但不允许点击去源码,这是一个主要问题。保持这种行为的唯一解决方案是将 绑定转换为原始函数。

绑定防止包含中间逻辑,因为这种逻辑会扰乱行号。但是,通过重新定义绑定函数并使用控制台 字符串替换字符串替换,仍然可以进行一些额外的操作。

这个要点 展示了一个极简的日志框架,它提供了34行的模块、日志级别、格式和适当的可单击行号。把它作为你自己需要的基础或灵感。

var log = Logger.get("module").level(Logger.WARN);
log.error("An error has occured", errorObject);
log("Always show this.");

编辑: 要点包括如下

/*
* Copyright 2016, Matthieu Dumas
* This work is licensed under the Creative Commons Attribution 4.0 International License.
* To view a copy of this license, visit http://creativecommons.org/licenses/by/4.0/
*/


/* Usage :
* var log = Logger.get("myModule") // .level(Logger.ALL) implicit
* log.info("always a string as first argument", then, other, stuff)
* log.level(Logger.WARN) // or ALL, DEBUG, INFO, WARN, ERROR, OFF
* log.debug("does not show")
* log("but this does because direct call on logger is not filtered by level")
*/
var Logger = (function() {
var levels = {
ALL:100,
DEBUG:100,
INFO:200,
WARN:300,
ERROR:400,
OFF:500
};
var loggerCache = {};
var cons = window.console;
var noop = function() {};
var level = function(level) {
this.error = level<=levels.ERROR ? cons.error.bind(cons, "["+this.id+"] - ERROR - %s") : noop;
this.warn = level<=levels.WARN ? cons.warn.bind(cons, "["+this.id+"] - WARN - %s") : noop;
this.info = level<=levels.INFO ? cons.info.bind(cons, "["+this.id+"] - INFO - %s") : noop;
this.debug = level<=levels.DEBUG ? cons.log.bind(cons, "["+this.id+"] - DEBUG - %s") : noop;
this.log = cons.log.bind(cons, "["+this.id+"] %s");
return this;
};
levels.get = function(id) {
var res = loggerCache[id];
if (!res) {
var ctx = {id:id,level:level}; // create a context
ctx.level(Logger.ALL); // apply level
res = ctx.log; // extract the log function, copy context to it and returns it
for (var prop in ctx)
res[prop] = ctx[prop];
loggerCache[id] = res;
}
return res;
};
return levels; // return levels augmented with "get"
})();

window.line = function () {
var error = new Error(''),
brower = {
ie: !-[1,], // !!window.ActiveXObject || "ActiveXObject" in window
opera: ~window.navigator.userAgent.indexOf("Opera"),
firefox: ~window.navigator.userAgent.indexOf("Firefox"),
chrome: ~window.navigator.userAgent.indexOf("Chrome"),
safari: ~window.navigator.userAgent.indexOf("Safari"), // /^((?!chrome).)*safari/i.test(navigator.userAgent)?
},
todo = function () {
// TODO:
console.error('a new island was found, please told the line()\'s author(roastwind)');
},
line = (function(error, origin){
// line, column, sourceURL
if(error.stack){
var line,
baseStr = '',
stacks = error.stack.split('\n');
stackLength = stacks.length,
isSupport = false;
// mac版本chrome(55.0.2883.95 (64-bit))
if(stackLength == 11 || brower.chrome){
line = stacks[3];
isSupport = true;
// mac版本safari(10.0.1 (12602.2.14.0.7))
}else if(brower.safari){
line = stacks[2];
isSupport = true;
}else{
todo();
}
if(isSupport){
line = ~line.indexOf(origin) ? line.replace(origin, '') : line;
line = ~line.indexOf('/') ? line.substring(line.indexOf('/')+1, line.lastIndexOf(':')) : line;
}
return line;
}else{
todo();
}
return '😭';
})(error, window.location.origin);
return line;
}
window.log = function () {
var _line = window.line.apply(arguments.callee.caller),
args = Array.prototype.slice.call(arguments, 0).concat(['\t\t\t@'+_line]);
window.console.log.apply(window.console, args);
}
log('hello');

这是我对这个问题的解决方案。当您调用 method: log 时,它将打印您打印日志的行号

我找到了一个简单的解决方案,将已接受的答案(绑定到 console.log/error/etc)与一些外部逻辑结合起来,以过滤实际记录的内容。

// or window.log = {...}
var log = {
ASSERT: 1, ERROR: 2, WARN: 3, INFO: 4, DEBUG: 5, VERBOSE: 6,
set level(level) {
if (level >= this.ASSERT) this.a = console.assert.bind(window.console);
else this.a = function() {};
if (level >= this.ERROR) this.e = console.error.bind(window.console);
else this.e = function() {};
if (level >= this.WARN) this.w = console.warn.bind(window.console);
else this.w = function() {};
if (level >= this.INFO) this.i = console.info.bind(window.console);
else this.i = function() {};
if (level >= this.DEBUG) this.d = console.debug.bind(window.console);
else this.d = function() {};
if (level >= this.VERBOSE) this.v = console.log.bind(window.console);
else this.v = function() {};
this.loggingLevel = level;
},
get level() { return this.loggingLevel; }
};
log.level = log.DEBUG;

用法:

log.e('Error doing the thing!', e); // console.error
log.w('Bonus feature failed to load.'); // console.warn
log.i('Signed in.'); // console.info
log.d('Is this working as expected?'); // console.debug
log.v('Old debug messages, output dominating messages'); // console.log; ignored because `log.level` is set to `DEBUG`
log.a(someVar == 2) // console.assert
  • 请注意,console.assert使用条件日志记录。
  • 请确保您的浏览器的开发工具显示所有消息级别!

这里有一种方法可以保持现有的 console日志语句,同时在输出中添加文件名和行号或其他堆栈跟踪信息:

(function () {
'use strict';
var isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;
var isChrome = !!window.chrome && !!window.chrome.webstore;
var isIE = /*@cc_on!@*/false || !!document.documentMode;
var isEdge = !isIE && !!window.StyleMedia;
var isPhantom = (/PhantomJS/).test(navigator.userAgent);
Object.defineProperties(console, ['log', 'info', 'warn', 'error'].reduce(function (props, method) {
var _consoleMethod = console[method].bind(console);
props[method] = {
value: function MyError () {
var stackPos = isOpera || isChrome ? 2 : 1;
var err = new Error();
if (isIE || isEdge || isPhantom) { // Untested in Edge
try { // Stack not yet defined until thrown per https://learn.microsoft.com/en-us/scripting/javascript/reference/stack-property-error-javascript
throw err;
} catch (e) {
err = e;
}
stackPos = isPhantom ? 1 : 2;
}


var a = arguments;
if (err.stack) {
var st = err.stack.split('\n')[stackPos]; // We could utilize the whole stack after the 0th index
var argEnd = a.length - 1;
[].slice.call(a).reverse().some(function(arg, i) {
var pos = argEnd - i;
if (typeof a[pos] !== 'string') {
return false;
}
if (typeof a[0] === 'string' && a[0].indexOf('%') > -1) { pos = 0 } // If formatting
a[pos] += ' \u00a0 (' + st.slice(0, st.lastIndexOf(':')) // Strip out character count
.slice(st.lastIndexOf('/') + 1) + ')'; // Leave only path and line (which also avoids ":" changing Safari console formatting)
return true;
});
}
return _consoleMethod.apply(null, a);
}
};
return props;
}, {}));
}());

然后像这样使用它:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script src="console-log.js"></script>
</head>
<body>
<script>
function a () {
console.log('xyz'); // xyz   (console-log.html:10)
}
console.info('abc'); // abc   (console-log.html:12)
console.log('%cdef', "color:red;"); // (IN RED:) // def   (console-log.html:13)
a();
console.warn('uuu'); // uuu   (console-log.html:15)
console.error('yyy'); // yyy   (console-log.html:16)
</script>
</body>
</html>

这在 Firefox、 Opera、 Safari、 Chrome 和 IE10(尚未在 IE11或 Edge 上测试)中都可以使用。

我解决这个问题的方法是创建一个对象,然后使用 定义属性()在该对象上创建一个新属性,并返回控制台属性,这个属性之前用作普通函数,但现在具有扩展功能。

var c = {};
var debugMode = true;


var createConsoleFunction = function(property) {
Object.defineProperty(c, property, {
get: function() {
if(debugMode)
return console[property];
else
return function() {};
}
});
};

然后,定义一个属性,你只需要..。

createConsoleFunction("warn");
createConsoleFunction("log");
createConsoleFunction("trace");
createConsoleFunction("clear");
createConsoleFunction("error");
createConsoleFunction("info");

现在你可以使用你的函数,就像

c.error("Error!");

一个小小的变化是让 debug ()返回一个函数,然后在需要它的地方执行—— debug (message)() ; 这样可以在控制台窗口中正确地显示正确的行号和调用脚本,同时允许重定向作为警报或保存到文件等变化。

var debugmode='console';
var debugloglevel=3;


function debug(msg, type, level) {


if(level && level>=debugloglevel) {
return(function() {});
}


switch(debugmode) {
case 'alert':
return(alert.bind(window, type+": "+msg));
break;
case 'console':
return(console.log.bind(window.console, type+": "+msg));
break;
default:
return (function() {});
}


}

因为它返回一个函数,所以该函数需要在调试行使用() ; 执行。其次,消息被发送到调试函数,而不是返回的函数,允许预处理或检查您可能需要的,如检查日志级状态,使消息更容易阅读,跳过不同类型,或只报告符合日志级标准的项目;

debug(message, "serious", 1)();
debug(message, "minor", 4)();

绑定 Function.prototype.bind的想法很棒。你也可以使用 npm 库 线路记录器。它显示了原始源文件:

在您的项目中创建一次记录器:

var LoggerFactory = require('lines-logger').LoggerFactory;
var loggerFactory = new LoggerFactory();
var logger = loggerFactory.getLoggerColor('global', '#753e01');

打印日志:

logger.log('Hello world!')();

enter image description here

基于其他答案(主要是@arctelix one) ,我为 Node ES6创建了这个应用程序,但是一个快速测试显示在浏览器中也有很好的结果。我只是传递另一个函数作为引用。

let debug = () => {};
if (process.argv.includes('-v')) {
debug = console.log;
// debug = console; // For full object access
}

此实现基于选定的答案,并有助于减少错误控制台中的噪声量: https://stackoverflow.com/a/32928812/516126

var Logging = Logging || {};


const LOG_LEVEL_ERROR = 0,
LOG_LEVEL_WARNING = 1,
LOG_LEVEL_INFO = 2,
LOG_LEVEL_DEBUG = 3;


Logging.setLogLevel = function (level) {
const NOOP = function () { }
Logging.logLevel = level;
Logging.debug = (Logging.logLevel >= LOG_LEVEL_DEBUG) ? console.log.bind(window.console) : NOOP;
Logging.info = (Logging.logLevel >= LOG_LEVEL_INFO) ? console.log.bind(window.console) : NOOP;
Logging.warning = (Logging.logLevel >= LOG_LEVEL_WARNING) ? console.log.bind(window.console) : NOOP;
Logging.error = (Logging.logLevel >= LOG_LEVEL_ERROR) ? console.log.bind(window.console) : NOOP;


}


Logging.setLogLevel(LOG_LEVEL_INFO);

下面是我的 logger 函数(基于一些答案)。希望有人能使用它:

const DEBUG = true;


let log = function ( lvl, msg, fun ) {};


if ( DEBUG === true ) {
log = function ( lvl, msg, fun ) {
const d = new Date();
const timestamp = '[' + d.getHours() + ':' + d.getMinutes() + ':' +
d.getSeconds() + '.' + d.getMilliseconds() + ']';
let stackEntry = new Error().stack.split( '\n' )[2];
if ( stackEntry === 'undefined' || stackEntry === null ) {
stackEntry = new Error().stack.split( '\n' )[1];
}
if ( typeof fun === 'undefined' || fun === null ) {
fun = stackEntry.substring( stackEntry.indexOf( 'at' ) + 3,
stackEntry.lastIndexOf( ' ' ) );
if ( fun === 'undefined' || fun === null || fun.length <= 1 ) {
fun = 'anonymous';
}
}
const idx = stackEntry.lastIndexOf( '/' );
let file;
if ( idx !== -1 ) {
file = stackEntry.substring( idx + 1, stackEntry.length - 1 );
} else {
file = stackEntry.substring( stackEntry.lastIndexOf( '\\' ) + 1,
stackEntry.length - 1 );
}
if ( file === 'undefined' || file === null ) {
file = '<>';
}


const m = timestamp + ' ' + file + '::' + fun + '(): ' + msg;


switch ( lvl ) {
case 'log': console.log( m ); break;
case 'debug': console.log( m ); break;
case 'info': console.info( m ); break;
case 'warn': console.warn( m ); break;
case 'err': console.error( m ); break;
default: console.log( m ); break;
}
};
}

例子:

log( 'warn', 'log message', 'my_function' );
log( 'info', 'log message' );

这里的所有解决方案都围绕着真正的问题转——调试器应该能够忽略堆栈的一部分,从而给出有意义的代码行。在编辑时,这个特性可以通过 js-debug 扩展的夜间构建获得。请参阅以下段落中的链接。

I在这里为 VSCode 的调试器提供了一个特性,它忽略位于启动配置的任何 skipFiles文件路径中的堆栈顶部。该属性位于 vscode 工作区的 launch.json配置中。因此,您可以创建一个文件/模块,负责包装 console.log,将其添加到 skipFiles中,调试器将显示调用到跳过的文件中的行,而不是 console.log 本身。

它在 js-debug 扩展的每晚构建中。看起来它可能会出现在下一个视觉工作室代码的次要版本中。我已经用夜间版本验证了它的工作原理。不再有变通方法了,耶!

听着,麦佛莱,这是我唯一的办法:

let debug = true;
Object.defineProperty(this, "log", {get: function () {
return debug ? console.log.bind(window.console, '['+Date.now()+']', '[DEBUG]')
: function(){};}
});


// usage:
log('Back to the future');
// outputs:
[1624398754679] [DEBUG] Back to the future

美妙之处在于避免了另一个函数调用,比如 log('xyz')() 或者创建一个包装对象甚至类。它也是 ES5安全的。

如果不需要前缀,只需删除参数。

Update 包含了为每个日志输出设置前缀的当前时间戳。

使用现代的 javascript 和 getter,您可以编写如下代码:

window.Logger = {
debugMode: true,
get info() {
if ( window.Logger.debugMode ) {
return window.console.info.bind( window.console );
} else {
return () => {};
}
}
}

它的好处在于,您可以同时打印出静态值和计算值,以及正确的行号。您甚至可以使用不同的设置来定义多个日志记录器:

class LoggerClz {
name = null;
debugMode = true;
constructor( name ) { this.name = name; }
get info() {
if ( this.debugMode ) {
return window.console.info.bind( window.console, 'INFO', new Date().getTime(), this.name );
} else {
return () => {};
}
}
}


const Logger1 = new LoggerClz( 'foo' );
const Logger2 = new LoggerClz( 'bar' );


function test() {
Logger1.info( '123' ); // INFO 1644750929128 foo 123 [script.js:18]
Logger2.info( '456' ); // INFO 1644750929128 bar 456 [script.js:19]
}
test();

您可以使用 可选的锁链来真正简化这个过程。您可以完全访问控制台对象,不需要任何修改和简洁的语法。

const debug = (true) ? console : null;


debug?.log('test');
debug?.warn('test');
debug?.error('test')

如果是 debug == null,则忽略 ?之后的所有内容,不抛出关于不可访问属性的错误。

const debug = (false) ? console : null;
debug?.error('not this time');

这还允许您直接使用调试对象作为除日志记录之外的其他调试相关进程的条件。

const debug = (true) ? console : null;


let test = false;


function doSomething() {
test = true;
debug?.log('did something');
}


debug && doSomething();


if (debug && test == false) {
debug?.warn('uh-oh');
} else {
debug?.info('perfect');
}


if (!debug) {
// set up production
}

如果需要,可以根据所需的日志级别使用 不行重写各种方法。

const debug = (true) ? console : null;
const quiet = true; const noop = ()=>{};


if (debug && quiet) {
debug.info = noop;
debug.warn = noop;
}


debug?.log('test');
debug?.info('ignored in quiet mode');
debug?.warn('me too');

对于具有正确行号的角度/打字控制台日志记录器,您可以执行以下操作:

示例文件 : < em > Console-logger. ts


export class Log {
static green(title: string): (...args: any) => void {
return console.log.bind(console, `%c${title}`, `background: #222; color: #31A821`);
}


static red(title: string): (...args: any) => void {
return console.log.bind(console, `%c${title}`, `background: #222; color: #DA5555`);
}


static blue(title: string): (...args: any) => void {
return console.log.bind(console, `%c${title}`, `background: #222; color: #5560DA`);
}


static purple(title: string): (...args: any) => void {
return console.log.bind(console, `%c${title}`, `background: #222; color: #A955DA`);
}


static yellow(title: string): (...args: any) => void {
return console.log.bind(console, `%c${title}`, `background: #222; color: #EFEC47`);
}
}

然后从其他文件调用它:

示例文件: < em > auth.service.ts

import { Log } from 'path to console-logger.ts';


const user = { user: '123' }; // mock data


Log.green('EXAMPLE')();
Log.red('EXAMPLE')(user);
Log.blue('EXAMPLE')(user);
Log.purple('EXAMPLE')(user);
Log.yellow('EXAMPLE')(user);

它在控制台中看起来是这样的: enter image description here

Stackblitz 的例子

这里没有我真正需要的东西,所以我添加了我自己的方法: 重写控制台并从一个合成的 Error读取原始错误行。该示例将控制台警告和错误存储到 console.appTrace,错误非常详细和冗长; 以这种简单的方式(console.appTrace.join("")告诉我从用户会话中需要的所有内容)。

注意,目前这只能在 Chrome 上使用

(function () {
window.console.appTrace = [];


const defaultError = console.error;
const timestamp = () => {
let ts = new Date(), pad = "000", ms = ts.getMilliseconds().toString();
return ts.toLocaleTimeString("cs-CZ") + "." + pad.substring(0, pad.length - ms.length) + ms + " ";
};
window.console.error = function () {
window.console.appTrace.push("ERROR ",
(new Error().stack.split("at ")[1]).trim(), " ",
timestamp(), ...arguments, "\n");
defaultError.apply(window.console, arguments);
};


const defaultWarn = console.warn;
window.console.warn = function () {
window.console.appTrace.push("WARN  ", ...arguments, "\n");
defaultWarn.apply(window.console, arguments);
};
})();

启发了 获取 node.js 中调用函数的名称和行和日期格式化方法在这个线程。