javascript 在不影响日志行的情况下扩展 console.log

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9559725/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 07:03:33  来源:igfitidea点击:

Extending console.log without affecting log line

javascriptgoogle-chrome

提问by Adam

I would like to extend the 'console.log' function to add additional information to its output - but I dont want to affect the script name/line number information generated by the browser in the console window. See how if I create my own implementation, I get useless trace information, should I want to locate that region of code... (they all link to the log implementation, not the actual script that caused the log message)

我想扩展 'console.log' 函数以向其输出添加其他信息 - 但我不想影响浏览器在控制台窗口中生成的脚本名称/行号信息。看看我如何创建自己的实现,我得到了无用的跟踪信息,我是否应该定位该代码区域...(它们都链接到日志实现,而不是导致日志消息的实际脚本)

enter image description here

在此处输入图片说明

Basically, my application is a very pluggable infrastructure, were any log output may occur within any number of frames. As such, I want every log message to include a special unique identifier at the beginning of the log message.

基本上,我的应用程序是一个非常可插拔的基础设施,任何日志输出都可能发生在任意数量的帧内。因此,我希望每条日志消息在日志消息的开头都包含一个特殊的唯一标识符。

I have tried replacing the console.log method with my own, but chrome complains with Uncaught TypeError: Illegal invocation

我曾尝试用我自己的方法替换 console.log 方法,但 chrome 抱怨 Uncaught TypeError: Illegal invocation

this is how I override it

这就是我覆盖它的方式

var orig = console.log;
console.log = function( message )
{
    orig( (window == top ? '[root]' : '[' + window.name + ']') + ': ' + message );
}

Any ideas?

有任何想法吗?

[EDIT] Note: After fixing the 'illegal invocation' problem, it seems the filename/linenumber is still 'polluted' by the override...

[编辑] 注意:修复“非法调用”问题后,似乎文件名/行号仍然被覆盖“污染”...

[EDIT] It looks like the general answer is - NO - despite some confusing goose chases, the desired functionality is NOT achievable in the current versions of browsers.

[编辑] 看起来一般的答案是 - 不 - 尽管有一些令人困惑的鹅追逐,但在当前版本的浏览器中无法实现所需的功能。

回答by kylehuff

Yes, it is possible to add information without messing up the originating line numbers of the log invocation. Some of the other answers here came close, but the trick is to have your custom logging method return the modified logger. Below is a simple example that was only moderately tested that uses the context variant.

是的,可以在不弄乱日志调用的原始行号的情况下添加信息。这里的其他一些答案很接近,但诀窍是让您的自定义日志记录方法返回修改后的记录器。下面是一个简单的例子,它只经过适度测试,使用了上下文变体。

log = function() {
    var context = "My Descriptive Logger Prefix:";
    return Function.prototype.bind.call(console.log, console, context);
}();

This can be used with:

这可以用于:

log("A log message..."); 

Here is a jsfiddle: http://jsfiddle.net/qprro98v/

这是一个 jsfiddle:http: //jsfiddle.net/qprro98v/

One could get easily get creative and pass the context variable in, and remove the auto-executing parens from the function definition. i.e. log("DEBUG:")("A debug message"), log("INFO:")("Here is some info"), etc.

可以轻松获得创意并传入上下文变量,并从函数定义中删除自动执行的括号。即 log("DEBUG:")("A debug message"), log("INFO:")("Here is some info") 等。

The only really import part about the function (in regards to line numbers) is that it returns the logger.

该函数唯一真正重要的部分(关于行号)是它返回记录器。

回答by Christopher Currie

If your use case can deal with a few restrictions, there isa way that this can be made to work. The restrictions are:

如果您的用例可以处理一些限制,则一种方法可以使其发挥作用。限制是:

  • The extra log content has to be calculated at bind time; it cannot be time sensitive or depend on the incoming log message in any way.

  • The extra log content can only be place at the beginning of the log message.

  • 额外的日志内容必须在绑定时计算;它不能对时间敏感或以任何方式依赖传入的日志消息。

  • 额外的日志内容只能放在日志消息的开头。

With these restrictions, the following may work for you:

有了这些限制,以下内容可能对您有用:

var context = "ALIASED LOG:"
var logalias;

if (console.log.bind === 'undefined') { // IE < 10
    logalias = Function.prototype.bind.call(console.log, console, context);
}
else {
    logalias = console.log.bind(console, context);
}

logalias('Hello, world!');

http://jsfiddle.net/Wk2mf/

http://jsfiddle.net/Wk2mf/

回答by Bradley

It is actually possible in chrome at least. Here is the most relevant. This may vary depending on setup, and how i got the splits was to just log the whole stack, and find the information I needed.

至少在 chrome 中实际上是可能的。这是最相关的。这可能因设置而异,我如何获得拆分只是记录整个堆栈,然后找到我需要的信息。

        var stack = new Error().stack;
        var file = stack.split("\n")[2].split("/")[4].split("?")[0]
        var line = stack.split("\n")[2].split(":")[5];

Here is the whole thing, preserving the native object logging.

这是整个事情,保留本机对象日志记录。

var orig = console.log
console.log = function(input) {
    var isChrome = navigator.userAgent.indexOf("Chrome") !== -1;
    if(isChrome){
        var stack = new Error().stack;
        var file = stack.split("\n")[2].split("/")[4].split("?")[0]
        var line = stack.split("\n")[2].split(":")[5];
        var append = file + ":" + line;
    }
    orig.apply(console, [input, append])
}

回答by ANisus

An acceptable solution can be to make your own log-function that returns a console.logfunction bound with the log arguments.

一个可接受的解决方案可以是创建自己的日志函数,该console.log函数返回一个与日志参数绑定的函数。

log = function() {
    // Put your extension code here
    var args = Array.prototype.slice.call(arguments);  
    args.unshift(console);
    return Function.prototype.bind.apply(console.log, args);
}

// Note the extra () to call the original console.log
log("Foo", {bar: 1})();

This way the console.logcall will be made from the correct line, and will be displayed nicely in the console, allowing you to click on it and everything.

这样,console.log呼叫将从正确的线路进行,并将在控制台中很好地显示,允许您单击它和所有内容。

回答by David Hellsing

You need to call the console.logwith the correct context (console):

您需要console.log使用正确的上下文 ( console)调用:

orig.call(console, message);

To complete your function allowing multiple arguments:

要完成允许多个参数的函数:

var orig = console.log;
console.log = function() {
    var msgs = [],
        prefix = (window== top ? '[root]' : '[' + window.name + ']');
    while(arguments.length) {
        msgs.push(prefix + ': ' + [].shift.call(arguments));
    }
    orig.apply(console, msgs);
};

Demo: http://jsfiddle.net/je2wR/

演示:http: //jsfiddle.net/je2wR/

Remember that you loose the built-in object/array browser in the console when combining objects with strings using the +sign.

请记住,当使用+号将对象与字符串组合时,您会丢失控制台中的内置对象/数组浏览器。

回答by Ben West

I just answered this on a post that helped me answer the original 'alias' question:

我刚刚在一篇帮助我回答原始“别名”问题的帖子上回答了这个问题:

(http://stackoverflow.com/a/12942764/401735)

(http://stackoverflow.com/a/12942764/401735)

my_log_alias = console.log.bind(console)

Apparently the capacity to do this has been designed in. Tested. Works.

显然已经设计了这种能力。经过测试。作品。

thereafter my_log_alias is the same as console.log and can be called in the same way; Calling this from inside the function will report the line number for that function call, including the line inside of an alias or advice function where applicable.

之后my_log_alias和console.log一样,可以用同样的方式调用;从函数内部调用 this 将报告该函数调用的行号,包括适用的别名或建议函数内部的行。

Specifically, the line number Chrome provides will tell you the file the line is in, so what you are doing may be unneccesary; Consider reporting this as a bug/feature request in chrome that it provide this info in console.log.

具体来说,Chrome 提供的行号会告诉您该行所在的文件,因此您所做的可能是不必要的;考虑将此报告为 chrome 中的错误/功能请求,它在 console.log 中提供此信息。

回答by Muxa

Christopher Currie provided an excellent solution. I've expanded it a bit for my needs. Here's the AMD module:

Christopher Currie 提供了一个很好的解决方案。我已经根据我的需要扩展了它。这是 AMD 模块:

define([], function () {

    var enableDebug = true;
    var separator = ">";    

    function bind(f, thisArg, ctx) {
        if (f.bind !== 'undefined') { // IE < 10
            return Function.prototype.bind.call(f, thisArg, ctx);
        }
        else {
            return f.bind(thisArg, ctx);
        }
    }

    function newConsole(context, parentConsole) {
        var log;
        var debug;
        var warn;
        var error;

        if (!parentConsole) {
            parentConsole = console;
        }

        context = context + separator;


        if (enableDebug) {
            debug = bind(console.log, console, context + "DEBUG" + separator);
        } else {
            debug = function () {
                // suppress all debug messages
            };
        }

        log = bind(console.log, console, context);

        warn = bind(console.warn, console, context);

        error = bind(console.error, console, context);

        return {
            debug: debug,
            info: log,
            log: log,
            warn: warn,
            error: error,
            /* access console context information */
            context: context,
            /* create a new console with nested context */
            nest: function (subContext) {
                return newConsole(context + subContext, this);
            },
            parent: parentConsole
        };
    }

    return newConsole("");
});

By default this will output > {message}. You can also add nested context to you logging, e.g. console.nest("my").log("test")will output >my> test.

默认情况下,这将输出> {message}. 您还可以向日志添加嵌套上下文,例如console.nest("my").log("test")将输出>my> test

I've also added a debugfunction that will indent messages with >DEBUG>

我还添加了一个debug函数来缩进消息>DEBUG>

Hope somebody will find it useful.

希望有人会觉得它有用。

回答by vaughan

I have looked into this several times and always found it was not possible.

我已经多次研究过这个问题,但总是发现这是不可能的。

My workaround if you are interested is to assign console to another variable and then wrap all my log messages in a function which lets me modify/style/whatever on the message.

如果您有兴趣,我的解决方法是将控制台分配给另一个变量,然后将我所有的日志消息包装在一个函数中,该函数允许我修改/样式/任何消息。

It looks nice with CoffeeScript, not sure its practical with plain JS.

CoffeeScript 看起来不错,但不确定它是否适用于普通的 JS。

I just get into the habit of prefixing everything with x.

我只是养成了用x.

logger.debug x 'Foo'

log x 'Bar'

log x('FooBar %o'), obj

回答by Daniel Sokolowski

Unfrotuantly it's currenlty not possible, In the future we might be able to do it with the Proxyobject in ECMAScript 6.

Unfrotuantly 目前是不可能的,将来我们可能可以使用ECMAScript 6 中的Proxy对象来做到这一点。

My use case was to auto-prefix console messages with helpful information like the arguments passed and executing method. at the moment the closest I got is using Function.prototype.apply.

我的用例是使用有用的信息(例如传递的参数和执行方法)自动为控制台消息添加前缀。目前我得到的最接近的是使用Function.prototype.apply.

A simple approachis to just write your debug statements as such:

一种简单的方法是编写调试语句,如下所示:

console.info('=== LazyLoad.css(', arguments, '): css files are skipped, gives us a clean slate to style within theme\'s CSS.');

A complicated approachis to use helper function as per below, I personally now prefer the simple approach.

一个复杂的方法是使用辅助函数如下,我个人现在更喜欢简单的方法。

Extending 'console.debug' function approach

扩展“console.debug”功能方法

/* Debug prefixing function
 * ===========================
 * 
 * A helper used to provide useful prefixing information 
 * when calling `console.log`, `console.debug`, `console.error`.
 * But the catch is that to utilize one must leverage the 
 * `.apply` function as shown in the below examples.
 *
 * ```
 * console.debug.apply(console, _fDebugPrefix(arguments)
 *    .concat('your message'));
 *
 * // or if you need to pass non strings
 * console.debug.apply(console, _fDebugPrefix(arguments)
 *    .concat('json response was:', oJson));
 *
 *
 * // if you need to use strict mode ("use strict") one can't
 * // extract the function name but following approach works very
 * // well; updating the name is just a matter of search and replace
 * var aDebugPrefix = ['fYourFunctionName('
 *                     ,Array.prototype.slice.call(arguments, 0), 
 *                     ,')'];
 * console.debug.apply(console, 
 *                     aDebugPrefix.concat(['json response was:', oJson]));
 * ```
 */
function _fDebugPrefix(oArguments) {
    try {
        return [oArguments.callee.name + '('
                ,Array.prototype.slice.call(oArguments, 0)
                , ')'];
    }
    catch(err) { // are we in "use strict" mode ?
        return ['<callee.name unsupported in "use strict">('
                ,Array.prototype.slice.call(oArguments, 0)
                , ')'];
    }
}

回答by Uncle Code Monkey

I ran into this issue as well about extending console.log() so that the application can extend, control and do fancy stuff with it in addition to logging stuff to the console. Losing the line number information was tantamount to failure, however. After wrestling with the issue, I came up with a long-winded workaround, but at least it's still a "1-liner" to use.

我也遇到了这个关于扩展 console.log() 的问题,以便应用程序除了将内容记录到控制台之外,还可以扩展、控制和使用它做一些有趣的事情。然而,丢失行号信息无异于失败​​。在解决这个问题之后,我想出了一个冗长的解决方法,但至少它仍然是一个“1-liner”可以使用。

First, define a global class to use or add some methods to your main existing "app" class:

首先,定义一个全局类以使用或向现有的主要“app”类添加一些方法:

/**
 * Log message to our in-app and possibly on-screen console, return args.
 * @param {!string} aMsgLevel - one of "log", "debug", "info", "warn", or "error"
 * @param {any} aArgs - the arguments to log (not used directly, just documentation helper)
 * @returns args so it can be nested within a console.log.apply(console,app.log()) statement.
 */
MyGlobalClassWithLogMethods.prototype.debugLog = function(aMsgLevel, aArgs) {
    var s = '';
    var args = [];
    for (var i=1; i<arguments.length; i++) {
        args.push(arguments[i]);
        if (arguments[i])
            s += arguments[i].toString()+' ';
    }
    if (typeof this.mLog === 'undefined')
        this.mLog = [];
    this.mLog.push({level: aMsgLevel, msg: s});
    return args;
};

MyGlobalClassWithLogMethods.prototype.log = function() {
    var args = ['log'].concat(Array.prototype.slice.call(arguments));
    return this.debugLog.apply(this,args);
};

MyGlobalClassWithLogMethods.prototype.debug = function() {
    var args = ['debug'].concat(Array.prototype.slice.call(arguments));
    return this.debugLog.apply(this,args);
};

MyGlobalClassWithLogMethods.prototype.info = function() {
    var args = ['info'].concat(Array.prototype.slice.call(arguments));
    return this.debugLog.apply(this,args);
};

MyGlobalClassWithLogMethods.prototype.warn = function() {
    var args = ['warn'].concat(Array.prototype.slice.call(arguments));
    return this.debugLog.apply(this,args);
};

MyGlobalClassWithLogMethods.prototype.error = function() {
    var args = ['error'].concat(Array.prototype.slice.call(arguments));
    return this.debugLog.apply(this,args);
};

//not necessary, but it is used in my example code, so defining it
MyGlobalClassWithLogMethods.prototype.toString = function() {
    return "app: " + JSON.stringify(this);
};

Next, we put those methods to use like so:

接下来,我们像这样使用这些方法:

//JS line done as early as possible so rest of app can use logging mechanism
window.app = new MyGlobalClassWithLogMethods();

//only way to get "line info" reliably as well as log the msg for actual page display;
//  ugly, but works. Any number of params accepted, and any kind of var will get
//  converted to str using .toString() method.
console.log.apply(console,app.log('the log msg'));
console.debug.apply(console,app.debug('the log msg','(debug)', app));
console.info.apply(console,app.info('the log msg','(info)'));
console.warn.apply(console,app.warn('the log msg','(warn)'));
console.error.apply(console,app.error('the log msg','(error)'));

Now the console gets log messages with their appropriate line information as well as our app contains an array of log messages that can be put to use. For example, to display your in-app log using HTML, JQuery and some CSS the following simplistic example can be used.

现在控制台获取带有相应行信息的日志消息,并且我们的应用程序包含一组可以使用的日志消息。例如,要使用 HTML、JQuery 和一些 CSS 显示您的应用内日志,可以使用以下简单示例。

First, the HTML:

首先,HTML:

<div id="debug_area">
    <h4 class="text-center">Debug Log</h4>
    <ul id="log_list">
        <!-- console log/debug/info/warn/error ('msg') lines will go here -->
    </ul>
</div>

some CSS:

一些CSS:

.log_level_log {
    color: black;
    background-color: white;
    font-size: x-small;
}
.log_level_debug {
    color: #060;
    background-color: #80FF80;
    font-size: x-small;
}
.log_level_info {
    color: #00F;
    background-color: #BEF;
    font-size: x-small;
}
.log_level_warn {
    color: #E65C00;
    background-color: #FB8;
    font-size: x-small;
}
.log_level_error {
    color: #F00;
    background-color: #FBB;
    font-size: x-small;
}

and some JQuery:

和一些 JQuery:

var theLog = app.mLog || [];
if (theLog.length>0) {
    var theLogList = $('#log_list');
    theLogList.empty();
    for (var i=0; i<theLog.length; i++) {
        theLogList.prepend($('<li class="log_level_'+theLog[i].level+'"></li>').text(theLog[i].msg));
    }
}

This is a simplistic use, but once you have the mechanism in place, you can do whatever your imagination can come up with, including leaving the log lines in the code, but setting a threshold so that only warnings and errors get through. Hopefully this helps others with their projects.

这是一个简单的用法,但是一旦你有了这个机制,你就可以做任何你能想到的事情,包括在代码中留下日志行,但设置一个阈值,以便只有警告和错误才能通过。希望这可以帮助其他人完成他们的项目。