记录 JavaScript 文件名和函数名

5
我希望我的node.js项目生成类似于log4j格式的日志文件,每个日志行都以文件名和js函数名开头,指示日志请求的来源。
例如: 如果我的js文件名为aNiceFile.js,我的js函数名为doImportantStuff(),我使用类似以下语句调用日志记录:
log.info('About to start on the important stuff')

我希望我的日志文件看起来像这样:
2018-03-14 06:33:26:619 INFO aNiceFile.js doImportantStuff() About to start on the important stuff.

我希望进行大量日志记录,因此我不介意一次性的前期工作来设置它,但是我希望每添加一个文件/函数时只需最少的额外工作量。今天我正在使用Winston,如果必要的话,我很乐意切换到其他东西,但是在使用Winston时,似乎需要我做出一些努力才能实现这一点:https://github.com/winstonjs/winston/issues/200。为了完整起见,我不需要行号,但也很好有。我的当前笨拙解决方法是:在每个文件中加入以下内容以获取当前文件名:
const sn = path.basename(__filename) // this script file name, used  for logging purposes

我很满意这一步,它并不繁琐,只需在每个文件顶部粘贴一行相同的文本即可,我可以接受这个。
2) 为了获取当前函数名称,请在每个函数前加上这行代码:
const fn = '<I copy/paste the function name into this string constant :( >'

我不喜欢这一步骤,我必须将函数名复制到字符串常量中,如果以后重命名该函数,则可能会失步。
如果我能将其转换为下面的版本,那就更好了,但我不确定如何做到这一点:
const fn = getCurrentFunctionName() 

3) 我会像这样记录每个日志语句:

log.info(`${sn}:${fn} Starting important stuff`)

我不喜欢这一步,因为我的所有日志语句都以此(${sn}:${fn})开头。
正如你所看到的,这很原始,但确实有效。我真正应该在这里做什么?
我对性能很感兴趣,因此需要生成错误对象来从中获取堆栈跟踪的解决方案可能不可接受。
2个回答

7

编辑添加所有内容。

这是一个关于文件名、行数、列数和调用函数的基本示例。也许您需要适应一些内容,但这就是思路。

let log = {
 info: function info(message) {
  const callerInfo = getFileName(info.caller.name);
  console.log(
   new Date() +
    ' ' +
    arguments.callee.name.toUpperCase() +
    ' ' +
    callerInfo.filename +
    ':' +
    callerInfo.line +
    ':' +
    callerInfo.column +
    ' ' +
    info.caller.name +
    '() ' +
    message
  );
 },
};

function getFileName(caller) {
  const STACK_FUNC_NAME = new RegExp(/at\s+((\S+)\s)?\((\S+):(\d+):(\d+)\)/);
 let err = new Error();
 
 Error.captureStackTrace(err);

 let stacks = err.stack.split('\n').slice(1);

 let callerInfo = null;
 for (let i = 0; i < stacks.length; i++) {
  callerInfo = STACK_FUNC_NAME.exec(stacks[i]);
    
  if (callerInfo[2] === caller) {
   return {
    filename: callerInfo[3],
    line: callerInfo[4],
    column: callerInfo[5],
   };
  }
 }

 return null;
}

function iWantToLog() {
 log.info('Testing my log');
}

iWantToLog();


谢谢@F.bernal,输出结果很完美。我唯一的问题是使用"let err = new Error();"语句,在性能方面会有一定的代价。我会尝试一下,并查看其对我的项目的性能影响。 - MattG
新的错误是获取调用栈和文件名以及行号。如果您可以通过其他方式获取文件名,则可以将其删除。 - F.bernal

2
一位同事建议使用Gulp预处理来解决这个问题。其想法是,在运行/部署代码之前,我不需要执行上述手动步骤1)2)和3),而是通过Gulp预处理将我的代码处理一遍,更新所有JS源代码,添加在步骤1)、2)和3)中所描述的所有代码。
我没有使用过Gulp,但乍一看,这听起来像一个有前途的想法。优点如下:
  1. 只要Gulp可以根据需要更新我的源文件,那么它就能正常工作。
  2. 不应引起任何重大的运行时JS性能问题,不需要生成JS错误对象来创建堆栈跟踪以从中提取脚本和函数名称。
  3. 我的源代码将不会被任何额外的代码淹没以记录脚本名和函数名。
缺点如下:
  1. 需要将Gulp作为我的工作流程的一部分-这似乎是可以接受的。
  2. 我需要设置gulp预处理程序来修改我的JS源代码-不知道这将有多难,是否可以从gulp预处理 程序开始?
  3. 当我对JS源文件进行代码更改时,每次都需要重新运行Gulp,这会影响我的迭代时间。

只要你的正则表达式技能还不错,使用 https://www.npmjs.com/package/gulp-replace 和 https://www.npmjs.com/package/gulp-header 应该不会太难。 - Mark

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接