如何获取Nodejs中显示的console.log行号?

Ang*_*loC 16 node.js

有一个旧的应用程序,使用打印出相当多的消息console.log,但我无法找到console.log调用哪些文件和行.

有没有办法挂钩应用程序并显示文件名和行号?

Dmi*_*nov 17

每次通话都有完整的堆栈跟踪有点吵.我刚刚改进了@noppa的解决方案,只打印启动器:

['log', 'warn', 'error'].forEach((methodName) => {
  const originalMethod = console[methodName];
  console[methodName] = (...args) => {
    let initiator = 'unknown place';
    try {
      throw new Error();
    } catch (e) {
      if (typeof e.stack === 'string') {
        let isFirst = true;
        for (const line of e.stack.split('\n')) {
          const matches = line.match(/^\s+at\s+(.*)/);
          if (matches) {
            if (!isFirst) { // first line - current function
                            // second line - caller (what we are looking for)
              initiator = matches[1];
              break;
            }
            isFirst = false;
          }
        }
      }
    }
    originalMethod.apply(console, [...args, '\n', `  at ${initiator}`]);
  };
});
Run Code Online (Sandbox Code Playgroud)

它还修补了其他方法(对Nodejs很有用,因为warnerror没有像Chrome中那样带有堆栈跟踪).

所以你的控制台看起来像:

Loading settings.json
   at fs.readdirSync.filter.forEach (.../settings.js:21:13)
Server is running on http://localhost:3000 or http://127.0.0.1:3000
   at Server.app.listen (.../index.js:67:11)
Run Code Online (Sandbox Code Playgroud)

  • 顺便说一句,使用 https://github.com/marak/colors.js/ 并进行此更改:“at ${initiator}` .green]”确实为您的线条提供了很好的颜色区分以提高可读性 (2认同)

nop*_*ppa 12

对于临时hack来查找要删除的日志语句,覆盖console.log自己并不困难.

var log = console.log;
console.log = function() {
    log.apply(console, arguments);
    // Print the stack trace
    console.trace();
};


// Somewhere else...
function foo(){
    console.log('Foobar');
}
foo();
Run Code Online (Sandbox Code Playgroud)

那将打印出类似的东西

Foobar
Trace
at Console.console.log (index.js:4:13)
at foo (index.js:10:13)
at Object.<anonymous> (index.js:12:1)
...
Run Code Online (Sandbox Code Playgroud)

那里的很多噪音,但是调用堆栈中的第二行at foo (index.js:10:13),应该指向正确的位置.

  • 另一种选择是直接使用`console.trace('Foobar');`,它将是一个行清除器,并且您保存了一个函数 (2认同)

Kas*_*ter 7

到目前为止,这个问题的所有解决方案都依赖于将堆栈跟踪拆分和匹配为一个字符串,这将在(不太可能的)情况下打破该字符串的格式在未来发生变化。受GitHub 上这个要点和这里的其他答案的启发,我想提供我自己的解决方案:

'use strict';

const path = require('path');

['debug', 'log', 'warn', 'error'].forEach((methodName) => {
    const originalLoggingMethod = console[methodName];
    console[methodName] = (firstArgument, ...otherArguments) => {
        const originalPrepareStackTrace = Error.prepareStackTrace;
        Error.prepareStackTrace = (_, stack) => stack;
        const callee = new Error().stack[1];
        Error.prepareStackTrace = originalPrepareStackTrace;
        const relativeFileName = path.relative(process.cwd(), callee.getFileName());
        const prefix = `${relativeFileName}:${callee.getLineNumber()}:`;
        if (typeof firstArgument === 'string') {
            originalLoggingMethod(prefix + ' ' + firstArgument, ...otherArguments);
        } else {
            originalLoggingMethod(prefix, firstArgument, ...otherArguments);
        }
    };
});

// Tests:
console.log('%s %d', 'hi', 42);
console.log({ a: 'foo', b: 'bar'});
Run Code Online (Sandbox Code Playgroud)

与其他解决方案不同,此脚本

你可以prefixchalkcolor.js 给它上色,但我不想在这里引入依赖关系。

上面的脚本使用V8 API 来自定义堆栈跟踪。该calleeCallSite在情况下,你要自定义的下列方法对象prefix

  • getThis: 返回值 this
  • getTypeName:this以字符串形式返回的类型。这是存储在 的构造函数字段中的函数名称(this如果可用),否则为对象的[[Class]]内部属性。
  • getFunction: 返回当前函数
  • getFunctionName: 返回当前函数的名称,通常是它的name属性。如果某个name属性不可用,则会尝试从函数的上下文中推断出一个名称。
  • getMethodName: 返回this保存当前函数的属性的名称或其原型之一
  • getFileName: 如果这个函数是在脚本中定义的,则返回脚本的名称
  • getLineNumber: 如果这个函数是在脚本中定义的,则返回当前行号
  • getColumnNumber: 如果此函数是在脚本中定义的,则返回当前列号
  • getEvalOrigin:如果这个功能是使用一个调用创建eval返回表示位置的字符串,其中eval被称为
  • isToplevel: 这是顶级调用,也就是说,这是全局对象吗?
  • isEval:此调用是否发生在由调用定义的代码中eval
  • isNative:这是原生 V8 代码中的调用吗?
  • isConstructor: 这是构造函数调用吗?
  • isAsync:这是一个异步调用(即awaitPromise.all())?
  • isPromiseAll: 这是对 的异步调用Promise.all()吗?
  • getPromiseIndex: 返回Promise.all()用于异步堆栈跟踪的 promise 元素的索引,或者null如果CallSite不是Promise.all()调用。

这个答案是我刚刚对一个类似问题给出的答案的交叉帖子,因为更多的人可能会找到这个页面。


Piy*_*yin 5

我发现Dmitry Druganov的回答确实不错,但是我在Windows 10(带有Node 8.9.4)上尝试了一下,但效果不佳。它正在打印完整路径,例如:

Loading settings.json
   at fs.readdirSync.filter.forEach (D:\Users\Piyin\Projects\test\settings.js:21:13)
Server is running on http://localhost:3000 or http://127.0.0.1:3000
   at Server.app.listen (D:\Users\Piyin\Projects\test\index.js:67:11)
Run Code Online (Sandbox Code Playgroud)

因此,我回答了我,并做出了以下改进(从我的观点):

  • 假设堆栈跟踪的重要行是第三行(第一行是单词Error,第二行是放置此脚本的位置)
  • 删除当前的脚本文件夹路径(由__dirname提供,在我的情况下为D:\Users\Piyin\Projects\test)。注意:为使此方法正常运行,脚本应位于项目的主Javascript上
  • 删除开始 at
  • 将文件信息放在实际日志之前
  • 将信息格式化为 Class.method at path/to/file:line:column

这里是:

['log','warn','error'].forEach((methodName) => {
  const originalMethod = console[methodName];
  console[methodName] = (...args) => {
    try {
      throw new Error();
    } catch (error) {
      originalMethod.apply(
        console,
        [
          (
            error
            .stack // Grabs the stack trace
            .split('\n')[2] // Grabs third line
            .trim() // Removes spaces
            .substring(3) // Removes three first characters ("at ")
            .replace(__dirname, '') // Removes script folder path
            .replace(/\s\(./, ' at ') // Removes first parentheses and replaces it with " at "
            .replace(/\)/, '') // Removes last parentheses
          ),
          '\n',
          ...args
        ]
      );
    }
  };
});
Run Code Online (Sandbox Code Playgroud)

这是新的输出:

fs.readdirSync.filter.forEach at settings.js:21:13
 Loading settings.json
Server.app.listen at index.js:67:11
 Server is running on http://localhost:3000 or http://127.0.0.1:3000
Run Code Online (Sandbox Code Playgroud)

这是最小化的代码(240字节):

['log','warn','error'].forEach(a=>{let b=console[a];console[a]=(...c)=>{try{throw new Error}catch(d){b.apply(console,[d.stack.split('\n')[2].trim().substring(3).replace(__dirname,'').replace(/\s\(./,' at ').replace(/\)/,''),'\n',...c])}}});
Run Code Online (Sandbox Code Playgroud)