javascript 保留行号并支持大多数方法的 console.log 包装器?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11308239/
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 12:42:18  来源:igfitidea点击:

console.log wrapper that keeps line numbers and supports most methods?

javascriptlogging

提问by Paul Weber

How can i write a console log wrapper that:

我如何编写控制台日志包装器:

  • Keeping the recorded line number and file name of the log statement intact
  • Provides access to all log severity methods (error, log, debug, ...) and shows them in the console as they where logged
  • does provide some fallback (for example calls the log method when the browser does not support error)
  • can be switched off in a central location, so I can switch off logging for production
  • does handle the case that no console exists, and does not throw errors
  • 保持日志语句的记录行号和文件名完整
  • 提供对所有日志严重性方法(错误、日志、调试等)的访问,并在控制台中显示它们的记录位置
  • 确实提供了一些后备(例如,当浏览器不支持错误时调用 log 方法)
  • 可以在中央位置关闭,所以我可以关闭生产日志
  • 确实处理不存在控制台的情况,并且不会抛出错误

Since logging in Java Script is so inconsistent, there must be some solution. Implementing it myself is a little bit tedious, but there seems to be no good library.

既然登录Java Script是如此不一致,那么必须有一些解决方案。自己实现有点繁琐,不过好像没有什么好的库。

I currently found this logger that provides all the features, but it does mess up the line numbers. http://benalman.com/projects/javascript-debug-console-log/

我目前发现这个提供所有功能的记录器,但它确实弄乱了行号。http://benalman.com/projects/javascript-debug-console-log/

回答by Tim Down

There is my own log4javascript, which has its own logging console but also provides a wrapper around console.log. It fulfils all your criteria except keeping line numbers intact, which is impossible to achieve if you're wrapping calls to console.log()etc. in another function.

有我自己的log4javascript,它有自己的日志记录控制台,但也提供了一个围绕console.log. 除了保持行号完整之外,它满足您的所有标准,如果您console.log()在另一个函数中包装对etc. 的调用,这是不可能实现的。

var log = log4javascript.getLogger("main");
var appender = new log4javascript.BrowserConsoleAppender();
log.addAppender(appender);
log.debug("Hello world");

回答by LeoR

I would also recommend log4javascript and explain how you can still keep the information about the printed filename and line, at least in Chrome.

我还建议使用 log4javascript 并解释如何至少在 Chrome 中保留有关打印文件名和行的信息。

I am not talking about changing the filename and line printed by Chrome but you can get to the information you are interested in and append it to the log statement. My solution has been a quick hack but I think with a little more work you can get nicely formatted log statements. It probably has also a heavy performance-impact, but since you won't leave your logs activated in production this shouldn't be too much of a problem.

我不是在谈论更改 Chrome 打印的文件名和行,但您可以获取您感兴趣的信息并将其附加到日志语句中。我的解决方案是一个快速的解决方案,但我认为通过更多的工作,您可以获得格式良好的日志语句。它可能还会对性能产生重大影响,但由于您不会在生产中激活日志,因此这应该不是什么大问题。

The Concept

这个概念

In Chrome you can create an Error object which provides a stack property that shows you your current stack location andsomewhere in the stack string you find the file and line number of your calling script.

在 Chrome 中,您可以创建一个 Error 对象,该对象提供一个堆栈属性,该属性显示您当前的堆栈位置,在堆栈字符串中的某处找到您调用脚本的文件和行号。

  > new Error().stack
  "Error
    at eval at <anonymous> (eval at evaluate (unknown source))
    at eval at evaluate (unknown source)
    at FrameMirror.evaluate (native)
    at Object.evaluate (unknown source)
    at Object._evaluateOn (unknown source)
    at Object._evaluateAndWrap (unknown source)
    at Object.evaluateOnCallFrame (unknown source)
    at meinAjaxAufruf (http://localhost:8080/numberajax.js:21:9)
    at HTMLInputElement.onkeyup (http://localhost:8080/numberajax.html:15:188)"

For a log4javascript call the stack trace might look something like this:

对于 log4javascript 调用,堆栈跟踪可能如下所示:

"Error
    at Object.append (http://localhost:8080/log4javascript_uncompressed.js:1921:17)
    at Object.doAppend (http://localhost:8080/log4javascript_uncompressed.js:1047:9)
    at Object.callAppenders (http://localhost:8080/log4javascript_uncompressed.js:647:27)
    at Object.log (http://localhost:8080/log4javascript_uncompressed.js:640:10)
    at Object.debug (http://localhost:8080/log4javascript_uncompressed.js:748:9)
    at meinAjaxAufruf (http://localhost:8080/numberajax.js:36:16)
    at HTMLInputElement.onkeyup (http://localhost:8080/numberajax.html:16:188)"

And the file and line that made the log4javascript call and that i am interested in is

进行 log4javascript 调用并且我感兴趣的文件和行是

at meinAjaxAufruf (http://localhost:8080/numberajax.js:36:16)

The Solution

解决方案

I am guessing that the stack depth from the script your interested in to where the actual consolecall happens is always the same. So now you simply have to find out where the BrowserConsoleAppendermakes its window.consoleaccess and add the line you are interested in to the formatted string. I did the following changes to log4javascript_uncompressed.js(version 1.4.2 line 1913):

我猜测从您感兴趣的脚本到实际console调用发生的位置的堆栈深度总是相同的。所以,现在你只需要找出其中BrowserConsoleAppender使得其window.console接入,并添加你有兴趣来格式化字符串行。我对log4javascript_uncompressed.js(版本 1.4.2 第 1913 行)进行了以下更改:

} else if (window.console && window.console.log) { // Safari and Firebug
        var formattedMesage = getFormattedMessage();

        //---my additions
        var isChrome = navigator.userAgent.indexOf("Chrome") !== -1;
        if(isChrome){
            var stack = new Error().stack;
            var lineAccessingLogger = stack.split("\n")[6];
            formattedMesage += "\n" + lineAccessingLogger;
        }
        //---

        // Log to Firebug using its logging methods or revert to the console.log
        // method in Safari
        if (window.console.debug && Level.DEBUG.isGreaterOrEqual(loggingEvent.level)) {
            window.console.debug(formattedMesage);
        } else if (window.console.info && Level.INFO.equals(loggingEvent.level)) {
        ...

Now instead of

现在而不是

17:53:22,872 DEBUG - sending /NumberServlet?zahl=1&text=
                                                 log4javascript.js:154

I get

我得到

17:55:53,008 DEBUG - sending /NumberServlet?zahl=1&text=

    at meinAjaxAufruf (http://localhost:8080/numberajax.js:36:16) log4javascript_uncompressed.js:1930

It sure isn't a nice solution :), but I get what I need.

这肯定不是一个好的解决方案:),但我得到了我需要的东西。

With a little more knowledge of the framework I suppose one could change the PatternLayout in a way that you can define how to print the file name/location and line number.

有了对框架的更多了解,我想可以通过一种方式更改 PatternLayout,您可以定义如何打印文件名/位置和行号。

editInstead of my prior solution I made some modifications to the PatternLayout.prototype.format function, so now I can use the additional option %l to define where and how I want to output the calling file and its line. I published my changes and a usage example as a Gist.

编辑而不是我之前的解决方案,我对 PatternLayout.prototype.format 函数做了一些修改,所以现在我可以使用附加选项 %l 来定义我想要输出调用文件及其行的位置和方式。我发布了我的更改和使用示例作为Gist

回答by mpr

We had this issue with our log wrapper also and it turns out there is a fantastic, simple workaround using partial function application:

我们的日志包装器也遇到了这个问题,结果证明使用部分函数应用程序有一个很棒的简单解决方法:

if(DEBUG_ENABLED && (typeof console != 'undefined')) {
    this.debug = console.log.bind(console);
}
else {
    this.debug = function(message) {};
}

With this, your browser will detect the correct line number and file of the source you wanted to log.

这样,您的浏览器将检测到您想要记录的源的正确行号和文件。

回答by drzaus

Crossposting from related question (A proper wrapper for console.log with correct line number?) but with updated solution to address multiple methods.

来自相关问题的交叉发布(具有正确行号的 console.log 的适当包装器?)但具有更新的解决方案以解决多种方法。



I liked @fredrik's answer, so I rolled it up with another answer which splits the Webkit stacktrace, and merged it with @PaulIrish's safe console.log wrapper. "Standardizes" the filename:lineto a "special object" so it stands out and looks mostly the same in FF and Chrome.

我喜欢@fredrik 的答案,所以我将它与另一个拆分 Webkit堆栈跟踪的答案一起汇总,并将其与@PaulIrish 的安全 console.log 包装器合并。将“标准化”filename:line为“特殊对象”,因此它在 FF 和 Chrome 中脱颖而出并且看起来几乎相同。

Testing in fiddle: http://jsfiddle.net/drzaus/pWe6W/9/

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

_log = (function (methods, undefined) {

    var Log = Error; // does this do anything?  proper inheritance...?
    Log.prototype.write = function (args, method) {
        /// <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>
        /// <param name="method" type="string">the console method to use:  debug, log, warn, info, error</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[method]) {
            if (console[method].apply) { console[method].apply(console, args); } else { console[method](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>

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

    // method builder
    var logMethod = function(method) {
        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), method); // turn into proper array & declare method to use
        };//--  fn  logMethod
    };
    var result = logMethod('log'); // base for backwards compatibility, simplicity
    // add some extra juice
    for(var i in methods) result[methods[i]] = logMethod(methods[i]);

    return result; // expose
})(['error', 'debug', 'info', 'warn']);//--- _log

回答by manikanta

To keep it simple, I've the below wrapper for console methods:

为了简单起见,我有以下控制台方法的包装器:

var noop = function () {};
window.consolex = {
    debug : window.console && window.console.debug && console.debug.bind(console) || noop,
    log : window.console && window.console.log && console.log.bind(console) || noop,
    warn: window.WARN = window.console && window.console.warn && console.warn.bind(console) || noop,
    error: window.ERROR = window.console && window.console.error && console.error.bind(console) || noop
};

Also, for better logs in IE and older browsers, please read: Detailed console logging

另外,为了在 IE 和旧浏览器中更好地登录,请阅读:详细的控制台日志记录

回答by Frison Alexander

Google Chrome will soon have a feature that will be of interest to this thread.

谷歌浏览器很快就会有一个这个线程会感兴趣的功能。

You can enable it now by:

您现在可以通过以下方式启用它:

  1. Enable chrome://flags/#enable-devtools-experiments
  2. Click on cog in dev tools
  3. Go to Experiments Tab
  4. Check "Javascript frameworks debugging"
  5. Go to General Tab
  6. Under the Sources section
  7. Check "Skip stepping through sources with particular names"
  8. In the pattern inputbox: type in the file name that you see now (app.log.js)
  1. 启用 chrome://flags/#enable-devtools-experiments
  2. 单击开发工具中的齿轮
  3. 转到实验选项卡
  4. 勾选“Javascript 框架调试”
  5. 转到常规选项卡
  6. 在“来源”部分下
  7. 选中“跳过具有特定名称的源”
  8. 在模式输入框中:输入您现在看到的文件名(app.log.js)

Restart and enjoy :)

重新启动并享受:)

References:

参考:

Tests from chrom devtools

来自 chrom devtools 的测试

devtools Issues thread

devtools 问题线程

devtools code review

开发者工具代码

回答by arctelix

I answered this question here, but in short see the codepenfor full implementation. However, this does everything you want, cross browser, no errors, correct line numbers, all available console methods, global and local control:

在这里回答了这个问题,但简而言之,请参阅完整实现的代码笔。但是,这可以满足您的所有要求,跨浏览器,没有错误,正确的行号,所有可用的控制台方法,全局和本地控制:

var Debugger = function(gState, klass) {
  this.debug = {}
  if (!window.console) return function(){}
  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
}

And use it like this:

并像这样使用它:

isDebug = true //global debug state

debug = Debugger(isDebug, this)

debug.log('Hello Log!')

回答by web site maker

I found a solution (requires jquery) somehwere on the Web but it does not work in most browsers. I changed it and it works in Firefox (Mac, Linux. Android), Chrome (Mac, Linux. Android) and Safari and other Android webkit browsers.

我在网上找到了一个解决方案(需要 jquery),但它在大多数浏览器中不起作用。我更改了它,它可以在 Firefox(Mac、Linux.Android)、Chrome(Mac、Linux.Android)和 Safari 以及其他 Android webkit 浏览器中运行。

Just write the following code to a file called e.g. debug.js and include it after the inclusion of 'jquery.js' in the <head> section of your webpage and it will work after the page has loaded(document.ready). I still have to find out to allow debugging beforeeverything is loaded (e.g. only the <head>...</head> ). The webpage has to be called with ?d=1 in the URL and when using Safari ?d=1s as I cannot make a distinction between Safari and another Webkit browser in the user agent and Safari has a different behavior in line number and file name handling than other Webkit browsers.

只需将以下代码写入名为例如 debug.js 的文件中,并在网页的 <head> 部分中包含 'jquery.js' 之后包含它,它会在页面加载(document.ready) 后工作。我仍然必须加载所有内容之前找出允许调试(例如,只有 <head>...</head> )。必须在 URL 中使用 ?d=1 调用网页,并且在使用 Safari 时 ?d=1s 因为我无法在用户代理中区分 Safari 和另一个 Webkit 浏览器,并且 Safari 在行号和文件名方面有不同的行为处理比其他 Webkit 浏览器。

The function p_r(expression) logs to the window of the id #js_debug and to the console (if opened) with the file name and line number.

函数 p_r(expression) 将文件名和行号记录到 id #js_debug 的窗口和控制台(如果打开)。

var g_d = null;


function sortObj(theObj)
{
  var sortable = [];
  for (var i in theObj) {
    sortable.push(i);
  }
  sortable.sort();

  var copy = new Object;
  for (var i in sortable) {
    var ind = sortable[i];
    copy[ind] = theObj[ind];
  }

  return copy;

}

function p_r(s, comment, level)
{
  if (!g_d) return;
  var res = s;
  var pre = new Array("","  " , "    ", "      ", "        ");
  if (comment) comment += ' : ';
  if (arguments.length<2) comment='';
  if (arguments.length<3) level = 0;
//  if (console) console.log(s);
  if (typeof(s) == 'object') {
    var copy = sortObj(s);
    comment += '\n';
    res = '[object]\n';
    if (level < 2) {
      for (var i in copy) {
        if (typeof(copy[i]) != "function")
          res += pre[level] + (i) + " : " + p_r(copy[i], '', level+1) +  " : " + typeof(copy[i]) + "\n";
      }
      res += pre[level] + "[/object]\n";
    }
  }
  else if (typeof(s) == 'function')
    res = 'function';
  else if (typeof(s) != 'string')
    res = '' + s;
  res = res.replace(/&/g, '&amp;');
  res = res.replace(/\x3C/g, '&lt;');
  res = res.replace(/>/g, '&gt;');
  if (level == 0) {
window.LOG=res;
console.log(window.LOG + comment + res);
    g_d.innerHTML += (window.LOG + comment + res + '\n');
  }
  return res;
}

if (location.href.match(/d\=[1-9]/)) {

  $(document).ready(function() {
    $("body").prepend("<div id=\"js_debugclick\" onclick=\"$('#js_debug').toggle();\">JS DEBUG</div>\
  <pre onclick=\"$('#js_debug').toggle();\" id='js_debug'></pre>\
");

    $("head").append("<style type=\"text/css\">\
pre#js_debug {\
border: solid black 1px; background-color: #1CF; color: #000; display:none; position:absolute; top: 20px;\
font-family: Lucida Console, monospace; font-size: 9pt; height: 400px; overflow:scroll; width:100%;\
z-index:100;\
} \
#js_debugclick { \
  color:red; font-weight:bold; \
} \
</style>\
");
    g_d = document.getElementById('js_debug');
  });

  var __moredebug = location.href.match(/d\=[2-9]/);

    var __issafari = /safari/.test(navigator.userAgent.toLowerCase()) && location.href.match(/d\=[1-9]s/);
    var __iswebkit = /webkit/.test(navigator.userAgent.toLowerCase());
    var __isopera  = /opera/.test(navigator.userAgent.toLowerCase());
  if (__moredebug) console.log(__issafari, __iswebkit);

/*@const*/ //for closure-compiler
//DEBUG=2 // 0=off, 1=msg:file:line:column, 2=msg:stack-trace

/*@const @constructor*/
Object.defineProperty(window,'__stack__',{get:function(){
    try{i.dont.exist()}catch(e){
if (__moredebug)  var x=e.stack.split(":"); for (i in x){console.log(i,x[i]);}
//    console.log(e.stack.split(":")[13].match(/(\d+)/)[1]);
    return e.stack.split(":")}
}})

/*@const @constructor*/
Object.defineProperty(window,'__file__',{get:function(){
    var s=__stack__,l=s.length
    var f= __issafari ? s[9] : (__isopera ? s[12] : (__iswebkit ? s[14] : s[9]));
    return f.replace(/^.+?\/([^\/]+?)\?.+?$/, "");
}})

/*@const @constructor*/
Object.defineProperty(window,'__line__',{get:function(){
    var s=__stack__,l=s.length
    return __issafari ? s[10].match(/(\d+)/)[1] :(__isopera ? s[13].match(/(\d+)/)[1] : (__iswebkit ? s[15] : s[10].replace(/\n/, " ").replace(/(\d+).+?$/, "")));
}})

/*@const @constructor*/
Object.defineProperty(window,'__col__',{get:function(){
    var s=__stack__,l=s.length
    return (isNaN(s[l-2]))?"NA":s[l-1]
}})

/*@const @constructor*/
Object.defineProperty(window,'LOG',{
    get:function(){return out},
    set:function(msg){if(0)out=msg+"\t-\t"+__stack__
        else out=__file__+" "+__line__+": ";
        }
})



}//end if(DEBUG)