自定义控制台日志函数,一个console.log的包装器

17
function log( msgOrObj ){
    if(dev_mode){
        console.log({
            'message': msgOrObj,
            'caller': arguments.callee.caller.toString()
        });
    }
}

所以,我尝试编写一个简单的自定义控制台日志函数(如上所述)。然而我在寻找调用者来自哪个文件和哪一行时遇到了困难。我所能看到的只有调用它的函数。

有人做过类似的事情吗?或者这是否可能?

在somescript.js中使用的示例位于第70行:

log('some very important message!')

6
如何获取 JavaScript 调用函数的行号?如何获取相关联的调用堆栈信息?要获取 JavaScript 中调用函数的行号,可以使用Error对象的stack属性。可以在函数中抛出一个错误,并捕获该错误并检查其stack属性。这将显示完整的调用堆栈信息,包括行号。例如,以下代码演示了如何获取调用函数的行号及其调用堆栈信息:function myFunction() { var error = new Error(); console.log(error.stack); } myFunction();输出结果类似于:Error at myFunction (http://localhost/example.js:4:9) at window.onload (http://localhost/example.js:8:1)这表明myFunction函数被调用时,在本地主机上的example.js文件的第4行和第9个字符处发生了错误。它还显示了在window.onload事件处理程序中调用myFunction函数的情况,该事件处理程序位于example.js文件的第8行和第1个字符处。 - nice ass
1
答案在页面上使用巧妙的技巧给出 :-) https://dev59.com/qGYr5IYBdhLWcg3wQYJa - DoXicK
如果您认为这是重复的(我也这么认为),您应该投票关闭它。 - Joe
1
请查看我在重复问题a-proper-wrapper-for-console-log-with-correct-line-number上的无hack答案。 - Arctelix
8个回答

4

是的,但这种方法很不规范,并且不适用于所有浏览器。您可以将其作为起点。它借鉴了这个答案

window.trace = function stackTrace() {
    var err = new Error();
    return err.stack;
}

window.my_log = function (x) {
    var line = trace();
    var lines = line.split("\n");
    console.log(x + " " + lines[2].substring(lines[2].indexOf("("), lines[2].lastIndexOf(")") + 1))
}


window.my_log("What light through yonder window breaks?")

生成:

What light through yonder window breaks? (<anonymous>:2:42) 

3

我见过的唯一可靠提取这种信息的方法是抛出一个错误,然后从堆栈跟踪中提取调用者信息,大致如下:

function log( msgOrObj ){
    if(dev_mode){

        try {
            in_val_id(); // force an error by calling an non-existent method
        catch(err) {
            // some regex/string manipulation here to extract function name
            // line num, etc. from err.stack
            var caller = ...
            var lineNo = ...
        }

        console.log({
            'message': msgOrObj,
            'caller': caller,
            'lineNo': lineNo
        });
    }
}

Chrome的堆栈形式如下:

ReferenceError: in_val_id is not defined
at log (<anonymous>:4:13)
at <anonymous>:2:14
at <anonymous>:2:28
at Object.InjectedScript._evaluateOn (<anonymous>:581:39)
at Object.InjectedScript._evaluateAndWrap (<anonymous>:540:52)
at Object.InjectedScript.evaluate (<anonymous>:459:21) 

你可以使用以下代码提取函数名:

caller = err.stack.split('\n')[3].split('at ')[1].split(' (')[0];

使用正则表达式可能更加高效。您可能需要不同的方法来提取不同浏览器中的此信息。

需要注意的是,抛出和处理错误是非常耗费资源的,因此以这种方式输出大量日志消息很可能会影响整体性能,但如果专门用于调试模式,则可能可以接受。


2

所以,最终我选择了这种方式(其中 shout 是一个仅在开发模式下运行的定制函数):

function log( msgOrObj ){
    if(dev_mode){
        if( typeof(window.console) != 'undefined' ){
            try {  invalidfunctionthrowanerrorplease(); }
            catch(err) {  var logStack = err.stack;  }
            var fullTrace = logStack.split('\n');
            for( var i = 0 ; i < fullTrace.length ; ++i ){
                fullTrace[i] = fullTrace[i].replace(/\s+/g, ' ');
            }
            var caller = fullTrace[1],
                callerParts = caller.split('@'),
                line = '';

            //CHROME & SAFARI
            if( callerParts.length == 1 ){
                callerParts = fullTrace[2].split('('), caller = false;
                //we have an object caller
                if( callerParts.length > 1 ){
                    caller = callerParts[0].replace('at Object.','');
                    line = callerParts[1].split(':');
                    line = line[2];
                }
                //called from outside of an object
                else {
                    callerParts[0] = callerParts[0].replace('at ','');
                    callerParts = callerParts[0].split(':');
                    caller = callerParts[0]+callerParts[1];
                    line = callerParts[2];
                }
            }
            //FIREFOX
            else {
                var callerParts2 = callerParts[1].split(':');
                line = callerParts2.pop();
                callerParts[1] = callerParts2.join(':');
                caller = (callerParts[0] == '') ? callerParts[1] : callerParts[0];
            }
            console.log( ' ' );
            console.warn( 'Console log: '+ caller + ' ( line '+ line +' )' );
            console.log( msgOrObj );
            console.log({'Full trace:': fullTrace });
            console.log( ' ' );
        } else {
            shout('This browser does not support console.log!')
        }
    }
}

如果在应用程序的其余部分之前声明log(),则可以从应用程序内的任何位置调用它,并提供开发人员所需的所有信息,而且不会在dev模式下运行。

(http://webconfiguration.blogspot.co.uk/2013/12/javascript-console-log-wrapper-with.html)


1

不使用参数,可以这样做:

function log( msg ) {
    if (dev_mode) {
        var e = new Error(msg);
        console.log(e.stack);
    }
}

这会显示所有函数被调用的顺序(包括行号和文件)。您可以忽略堆栈的前两行(其中一行将包含错误消息,另一行将包含日志函数,因为您正在函数内创建错误对象)。
如果您想要更强大的日志记录-使用 @DoXicK 建议的 一个正确行号的 console.log 适当的包装器?

1

我在Node中使用这个方法,效果特别好。console.log只是一个函数,它可以被重新分配以及存储起来,在我们完成后返回。我没有理由认为这在浏览器中不起作用。

//Store console.log function in an object so
//we can still use it.
theConsole = {};
theConsole.log = console.log;

//This function is called when console.log occurs
//arguments[0] is what would normally be printed.
console.log = function(){
    theConsole.log(">" + arguments[0]);
}

//Call our console.log wrapper
console.log("Testing testing 123");
console.log("Check one two");

//Put back the way it was
console.log = theConsole.log;
console.log("Now normal");

0

试试这个

window.log = (() => {
  if (dev_mode) {
    return console.log;
  } else return () => {};
})();

0

有几种快速处理的选项。

1 - 使用console.error 不太方便,实际错误会被忽略,并且在控制台输出中看到大量红色可能会对您的士气产生负面影响。简而言之——除非是用于非常小的脚本或某些测试,否则不要使用。

2 - 将您的日志方法添加到对象的原型中 以获取当前作用域/模块名称/等等。更加灵活和优雅。

Object.prototype.log = function(message){
    console.log({
        'message': message,
        'caller': this, 
        'stack':arguments.callee.caller.toString()
    });
};

使用 (任何地方) 如下:

this.log("foo");

您可以将this线程中的技术添加到您的对象中,以获取精确的函数名称,如下所示:

    var callerFunc = arguments.callee.caller.toString();
    callerFuncName = (callerFunc.substring(callerFunc.indexOf("function") + 9, callerFunc.indexOf("(")) || "anoynmous");

但请确保您的作用域已命名... 这将迫使您从以下内容开始:

Module.method = function(){}

变为这样:

Module.method = function method(){}

关于行号,调用 (new Error()) 将使您可以访问调用它的行号——并且并非在所有浏览器中都是如此。
创建一个优雅的调试函数需要一些工作。
尽管我不愿承认,但另一个答案暗示使用正则表达式来处理 try 结果似乎是解决您问题的更快方法。

0

看起来你们都在太苦苦挣扎了。我有一个简单的一行代码解决方案:

//Just do this, that I have done below: HAVE FUN

var log=console.log;

log(`So this way is known as Aniket's way.`);
log(`Don't be too serious this is just the fun way of doing same thing`);

log(`Thank You`)


2
嗨,欢迎来到StackOverflow。 虽然你写的代码能够工作,但我认为它不是这里问题的答案,因为它并不是一个自定义的日志函数,而只是将原始的console.log重命名。 编辑:当然,如果我没有误解原来的问题的话。 - WolfyD

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接