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

时间:2014-09-12 09:16:37

标签: javascript node.js debugging optimization dead-code

对于我正在写的某个班级,表现很重要。

我想过调用一个这样的函数:

debug('This is a debug message, only visible when debugging is on');

内容就像

function debug(message) {
    if (DEBUG) console.log(message);
}

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

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

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

3 个答案:

答案 0 :(得分:1)

我使用注释来缩小文件时删除,例如:

function name(arg) {
    // <debug>
    if (!arg) {
        throw new Error("arg must be defined"); 
    }
    // </debug>
    ... code goes here
}

例如:https://github.com/PANmedia/raptor-editor/blob/master/src/raptor-widget.js#L29-L33

我的(自定义)构建脚本,用于执行前面提到的https://github.com/PANmedia/raptor-build/blob/master/build/raptor-builder.js#L305-L313

答案 1 :(得分:1)

有几种解决方案可用(除了Petah's ...):

  1. 使用UglifyJS2 conditional compilation
  2.   

    您可以使用--define(-d)开关来声明全局   UglifyJS将假定为常量的变量(除非在   范围)。例如,如果你传递--define DEBUG = false然后,耦合   删除死代码UglifyJS将丢弃以下内容   输出:

    if (DEBUG) {
        console.log("debug stuff");
    }
    
         

    UglifyJS将警告这种情况总是错误的   删除无法访问的代码;目前没有选项只能关闭   这个特定的警告,您可以传递warnings = false来关闭所有   警告。

         

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

    const DEBUG = false;
    const PRODUCTION = true;
    // etc.
    and build your code like this:
    
         

    uglifyjs build / defines.js js / foo.js js / bar.js ... -c UglifyJS将   注意常数,因为它们不能改变,它会   评估对值本身的引用并删除无法访问   代码照常。这种方法的可能缺点是   build将包含const声明。

    1. 使用包装函数。
    2. 例如,你有这个方法:

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

      通过将记录包装在记录器功能中来添加日志记录:

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

      这种方式唯一的性能影响将是在启动时。您还可以使用AOP方法和上面的包装函数:

      exports.complicatedMethod = function (arg1, arg2, arg3) {
          stuff...
      };
      
      if (DEBUG) {
          for (var name in exports) {
             exports[name] = logger(exports[name]);
          }
      }
      

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

      exports.complicatedMethod.description = 'This function only shows how tired i was when I was writing it and nothing else!';
      

      您可以查看this question,其中某人创建了代码,该代码以递归方式为对象中的函数创建记录器。另请查看this answer of mine

      1. 使用C Pre Processor
      2. 你可以这样做:

        #if DEBUG
          console.log("trace message");
        #endif
        

        或类似的东西

        #if DEBUG
        #define DEBUG_LOG(x) console.log(x);
        #else
        #define DEBUG_LOG(x) //console.log(x);
        #endif
        

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

        DEBUG_LOG('put a random message her to confuse sys admins!')
        

        或者你使用它的npm warapper:laudanumscript

        1. 创建sweetjs macro
        2. 我无法使用sweetjs找到条件编译,但我确信实现它并不会太难。结束语法将是(或应该是!)类似于cpp。

答案 2 :(得分:0)

您可以使用支持以下内容的记录器库:

  1. 记录级别
  2. 后期绑定功能
  3. 示例:https://github.com/duongchienthang/js-logger