如何在不影响性能的情况下向函数添加详细日志记录代码?

ske*_*rit 7 javascript debugging optimization dead-code node.js

对我正在写的某个班级来说,表现很重要.

我想过调用这样的函数:

debug('This is a debug message, only visible when debugging is on');
Run Code Online (Sandbox Code Playgroud)

内容就像

function debug(message) {
    if (DEBUG) console.log(message);
}
Run Code Online (Sandbox Code Playgroud)

所以我想知道:如果DEBUG变量永远不变,V8是否足以将其标记为"死代码" ?

编辑:我更担心Node中的性能而不是浏览器,因此在缩小时删除代码是不够的.

Edit2:我从提议的解决方案中做了一个JSPerf基准测试,它们非常令人惊讶:http://jsperf.com/verbose-debug-loggin-conditionals-functions-and-no-ops/3

Far*_*hat 1

有几个可用的解决方案(除了Petah 的......):

  1. 使用UglifyJS2 条件编译

您可以使用 --define (-d) 开关来声明 UglifyJS 将假定为常量的全局变量(除非在作用域中定义)。例如,如果您传递 --define DEBUG=false 那么,再加上死代码删除,UglifyJS 将从输出中丢弃以下内容:

if (DEBUG) {
    console.log("debug stuff");
}
Run Code Online (Sandbox Code Playgroud)

UglifyJS 将警告条件始终为 false 并删除无法访问的代码;目前没有选项可以仅关闭此特定警告,您可以通过 warnings=false 来关闭所有警告。

另一种方法是将全局变量声明为单独文件中的常量并将其包含到构建中。例如,您可以有一个包含以下内容的 build/defines.js 文件:

const DEBUG = false;
const PRODUCTION = true;
// etc.
and build your code like this:
Run Code Online (Sandbox Code Playgroud)

uglifyjs build/defines.js js/foo.js js/bar.js... -c UglifyJS 会注意到常量,并且由于它们无法更改,因此它将评估对它们的引用到值本身,并像往常一样删除无法访问的代码。这种方法可能的缺点是构建将包含 const 声明。

  1. 使用包装函数。

例如你有这个方法:

exports.complicatedMethod = function (arg1, arg2, arg3) {
    stuff...
};
Run Code Online (Sandbox Code Playgroud)

您可以通过将其包装在记录器函数中来添加日志记录:

function logger(fn) {
    if (!DEBUG) {
        return fn;
    }
    return function () {
        console.log(fn.name, arguments); // You can also use `fn.toString()` to get the argument names.
        fn.apply(this, arguments);
    };
}

exports.complicatedMethod = logger(function (arg1, arg2, arg3) {
    stuff...
});
Run Code Online (Sandbox Code Playgroud)

这样,唯一的性能影响将发生在启动时。您还可以将AOP方法与上述包装函数一起使用:

exports.complicatedMethod = function (arg1, arg2, arg3) {
    stuff...
};

if (DEBUG) {
    for (var name in exports) {
       exports[name] = logger(exports[name]);
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以通过向函数添加属性来将信息传递给记录器:

exports.complicatedMethod.description = 'This function only shows how tired i was when I was writing it and nothing else!';
Run Code Online (Sandbox Code Playgroud)

您可以看一下这个问题,其中有人创建了为对象中的函数递归创建记录器的代码。另请检查我的这个答案

  1. 使用C 预处理器

你可以这样做:

#if DEBUG
  console.log("trace message");
#endif
Run Code Online (Sandbox Code Playgroud)

或者类似的东西

#if DEBUG
#define DEBUG_LOG(x) console.log(x);
#else
#define DEBUG_LOG(x) //console.log(x);
#endif
Run Code Online (Sandbox Code Playgroud)

然后你可以在你的代码中执行此操作

DEBUG_LOG('put a random message her to confuse sys admins!')
Run Code Online (Sandbox Code Playgroud)

或者你使用它的 npm warapper:laudanumscript

  1. 创建一个sweetjs 宏

我还没能找到 sweetjs 的条件编译,但我确信实现它不会太难。最终语法将(或应该是!)类似于 cpp。