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

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

A proper wrapper for console.log with correct line number?

javascript

提问by Rufus

I'm now developing an application, and place a global isDebugswitch. I would like to wrap console.logfor more convenient usage.

我现在正在开发一个应用程序,并放置一个全局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.');

Then I get this result in Firefox console.

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

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

What if I want to log with line number where debug()gets called, like info: Here is a msg. main.js (line 2)?

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

采纳答案by arctelix

This is an old question and All the answers provided are overly hackey, have MAJOR cross browser issues, and don't provide anything super useful. This solution works in every browser and reports all console data exactly as it should. No hacks required and one line of code Check out the codepen.

这是一个老问题,提供的所有答案都过于骇人听闻,存在重大的跨浏览器问题,并且没有提供任何非常有用的信息。此解决方案适用于每个浏览器,并完全按照应有的方式报告所有控制台数据。无需 hacks 和一行代码查看 codepen

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

Create the switch like this:

像这样创建开关:

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

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

Then simply call as follows:

然后简单地调用如下:

debug('This is happening.')

You can even take over the console.log with a switch like this:

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

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

If you want to do something useful with that.. You can add all the console methods and wrap it up in a reusable function that gives not only global control, but class level as well:

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

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!')

Now you can add it to your classes:

现在您可以将其添加到您的课程中:

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

回答by drzaus

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/

小提琴测试: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


This also works in node, and you can test it with:

这也适用于节点,您可以使用以下命令对其进行测试:

// 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 ---');

回答by namuol

You can maintain line numbers andoutput the log level with some clever use of Function.prototype.bind:

您可以巧妙地使用以下方法来维护行号输出日志级别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)

Taking it a step further, you could make use of the console's error/warning/info distinctions and still have custom levels. Try it!

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

function setDebug(isDebug) {
  if (isDebug) {
    window.debug = {
      log: window.console.log.bind(window.console, '%s: %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.'); // -> 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)

回答by fredrik

From: How to get JavaScript caller function line number? How to get JavaScript caller source URL?the Errorobject has a line number property(in FF). So something like this should work:

来自:如何获取 JavaScript 调用函数行号?如何获取 JavaScript 调用者源 URL?Error对象具有行号属性(在 FF 中)。所以这样的事情应该有效:

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

In Webkit browser you have err.stackthat is a string representing the current call stack. It will display the current line number and more information.

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

UPDATE

更新

To get the correct linenumber you need to invoke the error on that line. Something like:

要获得正确的行号,您需要调用该行上的错误。就像是:

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);

回答by Brian Grinstead

A way to keep line number is here: https://gist.github.com/bgrins/5108712. It more or less boils down to this:

保留行号的方法在这里: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);
    };
}

You could wrap this with isDebugand set window.logto function() { }if you aren't debugging.

如果您不进行调试,您可以将其包装起来isDebug并设置window.logfunction() { }

回答by Subodh

You can pass the line number to your debug method, like this :

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

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

Here, (new Error).lineNumberwould give you the current line number in your javascriptcode.

在这里,(new Error).lineNumber将为您提供javascript代码中的当前行号。

回答by kzahel

Chrome Devtools lets you achieve this with Blackboxing. You can create console.log wrapper that can have side effects, call other functions, etc, and still retain the line number that called the wrapper function.

Chrome Devtools 可让您通过Blackboxing实现这一点。您可以创建具有副作用、调用其他函数等的 console.log 包装器,并且仍然保留调用包装器函数的行号。

Just put a small console.log wrapper into a separate file, e.g.

只需将一个小的 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)
    }
})()

Name it something like log-blackbox.js

将其命名为 log-blackbox.js

Then go to Chrome Devtools settings and find the section "Blackboxing", add a pattern for the filename you want to blackbox, in this case log-blackbox.js

然后转到 Chrome Devtools 设置并找到“Blackboxing”部分,为要黑盒的文件名添加一个模式,在本例中为 log-blackbox.js

回答by Jacob Phillips

I found a simple solution to combine the accepted answer (binding to console.log/error/etc) with some outside logic to filter what is actually logged.

我找到了一个简单的解决方案,将接受的答案(绑定到 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;

Usage:

用法:

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
  • Note that console.assertuses conditional logging.
  • Make sure your browser's dev tools shows all message levels!
  • 请注意,console.assert使用条件日志记录。
  • 确保浏览器的开发工具显示所有消息级别!

回答by solendil

Stack trace solutions displaythe line number but do not allow to click to go to source, which is a major problem. The only solution to keep this behaviour is to bindto the original function.

Stack trace 解决方案显示行号但不允许单击转到源,这是一个主要问题。保持这种行为的唯一解决方案是绑定到原始函数。

Binding prevents to include intermediate logic, because this logic would mess with line numbers. However, by redefining bound functions and playing with console string substitution, some additional behaviour is still possible.

绑定会阻止包含中间逻辑,因为此逻辑会与行号混淆。然而,通过重新定义绑定函数和使用控制台字符串替换,一些额外的行为仍然是可能的。

This gistshows a minimalistic logging framework that offers modules, log levels, formatting, and proper clickable line numbers in 34 lines. Use it as a basis or inspiration for your own needs.

这个要点显示了一个简约的日志框架,它提供了模块、日志级别、格式和 34 行的正确可点击行号。将其用作满足您自己需求的基础或灵感。

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

回答by Alvin Wong

If you simply want to control whether debug is used and have the correct line number, you can do this instead:

如果你只是想控制是否使用 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(){}
    };
}

When you need access to debug, you can do this:

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

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

If isDebug == true, The line numbers and filenames shown in the console will be correct, because debug.logetc is actually an alias of console.logetc.

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

If isDebug == false, no debug messages are shown, because debug.logetc simply does nothing (an empty function).

如果isDebug == false,则不显示调试消息,因为debug.logetc 根本不执行任何操作(一个空函数)。

As you already know, a wrapper function will mess up the line numbers and filenames, so it's a good idea to prevent using wrapper functions.

如您所知,包装函数会弄乱行号和文件名,因此最好避免使用包装函数。