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

IT技术 javascript
2021-02-08 17:04:07

我现在正在开发一个应用程序,并放置一个全局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)怎么办,比如

6个回答

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

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')
}
@ABCarroll 您可以console.log通过将log()包含两次调用的自定义函数绑定到 两次console.log,但是行号将反映console.log实际驻留的行,而不是debug.log被调用的位置。但是,您可以执行添加动态前缀/后缀等操作。还有一些方法可以弥补行号问题,但我认为这是另一个问题。以这个项目为例:github.com/arctelix/iDebugConsole/blob/master/README.md
2021-03-15 17:04:07
Totalnoob.com/...该解决方案允许在自定义控制台日志中添加额外的文本作为前缀
2021-03-17 17:04:07
如果我错了,请纠正我,但这不允许您添加任何其他功能,对吗?您本质上只是为控制台对象设置了别名?一个粗略的例子 - 没有办法对每个 debug.log() 两次 console.log() 事件?
2021-03-20 17:04:07
这并没有回答“如果我想使用 debug() 被调用的行号记录怎么办?”的问题。
2021-03-31 17:04:07
此方法在 Firefox 47 到 49 版本(含)中不起作用。并且仅在 50.0a2 版本中修复。好吧,FF50 将在 2 周内发布,但我花了几个小时才明白为什么它不起作用。所以我认为这些信息可能对某人有所帮助。关联
2021-04-12 17:04:07

我喜欢@fredrik 的答案,所以我将它与另一个拆分 Webkit堆栈跟踪的答案一起汇总,并将其与@PaulIrish 的安全 console.log 包装器合并将“标准化”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

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

// 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 ---');
我的一位同事将您的代码复制粘贴到我们的项目中。它在 IE11 和 Safari 5 中破坏了站点。不确定此浏览器的其他版本。也许你会为未来的复制粘贴添加一个检查?
2021-03-13 17:04:07
一个更高级的答案占了额外的console方法,如warnerror等- stackoverflow.com/a/14842659/1037948
2021-03-25 17:04:07
@sigod 现在呢?添加if(!stack) return '?'到失败的方法,而不是它被调用的地方(所以如果有人使用方法本身,他们也被“保护”)
2021-04-01 17:04:07
@sigod——可能要么取决于浏览器,要么是我 2 年前写的,浏览器已经改变。你的情况是什么?
2021-04-03 17:04:07
var line = stack.split('\n')[2];'undefined' is not an object
2021-04-09 17:04:07

您可以巧妙地使用以下方法维护行号输出日志级别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, '%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)
我一直在研究大量的控制台包装器/垫片/等。这是我遇到的第一个将保留行号与自定义输出相结合的方法。这是对.bind 也为您做了一些柯里化这一事实的巧妙利用,除了 context 之外,您还可以绑定一个或多个参数您可以更进一步,将带有 .toString 方法的 noop 函数传递给它,该方法可以在调用 log 方法时运行代码!看到这个jsfiddle
2021-03-26 17:04:07
我已经尝试了一段时间来自动为输出console.debug(...)加上前缀function namearguments- 关于如何做到这一点的任何想法?
2021-04-05 17:04:07
也许不是在所有浏览器中(还没有研究过),但是在 Chrome 中替换%swith%o将按照您期望的方式打印参数(对象是可扩展的,数字和字符串是彩色的,等等)。
2021-04-07 17:04:07
喜欢这个解决方案。我做了一些更适合我的应用程序的更改,但其中大部分仍然完好无损并且运行良好。谢谢
2021-04-12 17:04:07

来自:如何获取 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);
将您的答案与其他几个答案结合起来——见下文stackoverflow.com/a/14841411/1037948
2021-03-26 17:04:07
new Error();给我它执行的上下文,如果我把它放进去debug.js,那么我会得到info: Here is a msg. file: http://localhost/js/debug.js line:7.
2021-03-29 17:04:07
有什么意义Log = Error你还在修改 Error 类,对吧?
2021-04-05 17:04:07

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