【问题标题】:How to remove debug statements from production code in Java如何从 Java 中的生产代码中删除调试语句
【发布时间】:2010-09-07 02:50:17
【问题描述】:

编译器是否可以从生产代码中删除用于调试目的(例如日志记录)的语句?调试语句需要以某种方式进行标记,可能使用注释。

设置属性 (debug = true) 并在每个调试语句中检查它很容易,但这会降低性能。如果编译器能让调试语句消失,那就太好了。

【问题讨论】:

    标签: java debugging compiler-construction


    【解决方案1】:

    这个“trick”似乎让你的调试语句消失了

    public static final boolean DEBUG = false;
    
    if (DEBUG) { //disapeared on compilation }
    

    post 表示javac 足够聪明,可以检查static final boolean 并排除调试语句。 (我没有亲自尝试过)

    对于日志记录,我个人不喜欢看到这样的代码:

    if (logger.isDebugEnabled()) {
        logger.debug("....");
    }
    realImportantWork();
    

    日志记录让我从realImportantWork() 中分心。对我来说正确的方法是:

    logger.debug("....");
    realImportantWork()
    

    加上排除所有生产调试消息的配置。

    我的意思是logger.isDebugEnabled() 控件应该是日志框架的工作,而不是我的工作。大多数日志框架都支持“logger”、“LogLevel”等概念。

    【讨论】:

      【解决方案2】:

      我也强烈推荐使用日志框架。

      logger.IsDebugEnabled()不是强制性的,只是在登录之前可以更快地检查系统是否处于调试级别。

      使用日志框架意味着您可以即时配置日志级别,而无需重新启动应用程序。

      你可以有这样的日志记录:

      logger.error("Something bad happened")
      logger.debug("Something bad happend with loads more detail")
      

      【讨论】:

        【解决方案3】:

        另一种可能性是将 if 语句放在你的日志记录函数中,这样你得到的代码更少,但代价是一些额外的函数调用。

        我也不喜欢完全删除调试代码。投入生产后,如果出现问题,您可能需要访问调试消息。如果您删除所有代码级调试,那么这是不可能的。

        【讨论】:

          【解决方案4】:

          两个建议。

          第一: 对于真正的日志记录,请使用现代日志记录包,如 log4j 或 java 自己的内置日志记录。不要太担心性能,日志级别检查是纳秒级的。 (这是一个整数比较)。

          如果您有多个日志语句,请保护整个块:

          (以log4j为例:)

          if (logger.isDebugEnabled()) {
          
            // perform expensive operations
            // build string to log
          
            logger.debug("....");
          }
          

          这为您提供了在运行时添加的能力控制日志记录。必须重新启动并运行调试版本可能非常不方便。

          第二:

          您可能会发现assertions 是您所需要的更多。断言是一个评估为布尔结果的语句,带有可选消息:

           assert (sky.state != FALLING) : "The sky is falling!";
          

          只要断言结果为假,断言就会失败并抛出包含您的消息的 AssertionError(这是一个未经检查的异常,旨在退出应用程序)。

          巧妙的是,这些被 JVM 特殊对待,并且可以在运行时切换到类级别,使用 VM 参数(无需重新编译)。如果未启用,则开销为零。

          【讨论】:

          • 以下是在 Eclipse 中启用断言的方法:link
          • 这不是答案。它没有解释如何使编译器的语句“消失”(因此在类文件中不可见)。你有这方面的一些信息吗?
          【解决方案5】:

          直接回答你的问题:我不知道。

          但这是您问题的另一种解决方案: 在我看来,这里有两个相互冲突的语句:“调试语句”和“生产代码”。

          调试语句的目的是什么?在(单元)测试时帮助摆脱错误。如果一个软件经过适当的测试并按照要求运行,那么调试语句就是过时的。

          我强烈反对在生产代码中留下任何调试语句。我敢打赌,没有人会费心在生产代码中测试调试代码的副作用。代码可能做了它应该做的事情,但它做的还不止这些吗?您所有的#defines 是否都能正常工作并真正消除所有调试代码?谁分析了 100000 行预处理代码,看看是否所有调试的东西都没有了?

          除非我们对生产代码有不同的定义,否则您应该考虑在测试代码后取出调试语句并完成它。

          【讨论】:

          • 如果您必须调试生产系统(没有调试器),生产代码中的调试语句可以成为救命稻草。而且软件永远不会“经过适当的测试和工作”,你只会越来越近......
          • 您想删除可能数百个调试语句?然后在您继续开发时将它们添加回来?抱歉,这就是为什么首先发明了#DEFINE。这也是 Java,而不是 C。如果您不知道,为什么要回答问题?
          【解决方案6】:

          Java 包含自己的某种预处理器。它被称为APT。它处理并生成代码。目前我不确定这应该如何工作(我没有尝试过)。但它似乎被用于这类事情。

          【讨论】:

            【解决方案7】:
            public abstract class Config
            {
                public static final boolean ENABLELOGGING = true;
            }
            

            import static Config.*;
            
            public class MyClass
            {
                public myMethod()
                {
                    System.out.println("Hello, non-logging world");
            
                    if (ENABLELOGGING)
                    {
                        log("Hello, logging world.");
                    }
                }
            }
            

            编译器将删除带有“Hello, logging world”的代码块。如果 ENABLE_LOGGING 设置为 true,则在其中,因为它是静态最终值。如果你使用混淆器比如 proguard,那么 Config 类也会消失。

            混淆器也允许这样的事情:

            public class MyClass
            {
                public myMethod()
                {
                    System.out.println("Hello, non-logging world");
            
                    Log.log("Hello, logging world.");
                }
            }
            

            import static Config.*;
            
            public abstract class Log
            {
                public static void log(String s)
                {
                    if (ENABLELOGGING)
                    {
                        log(s);
                    }
                }
            }
            

            方法 Log#log 将在编译器中归零,并被混淆器删除,以及对该方法的任何调用,最终甚至 Log 类本身也会被删除。

            【讨论】:

              【解决方案8】:

              使用Java Preprocessor? (google foo low 但这是一个链接到旧的 Joel 论坛讨论它)

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2016-04-08
                • 1970-01-01
                • 2013-04-08
                • 2011-11-15
                • 2015-06-14
                • 1970-01-01
                相关资源
                最近更新 更多