如何从Java中的生产代码中删除调试语句

时间:2008-08-28 11:40:58

标签: java debugging compiler-construction

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

设置属性(debug = true)很容易,并在每个调试语句中检查它,但这会降低性能。如果编译器只是简单地使调试语句消失,那将是很好的。

8 个答案:

答案 0 :(得分:23)

两项建议。

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

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

(log4j,例如:)

if (logger.isDebugEnabled()) {

  // perform expensive operations
  // build string to log

  logger.debug("....");
}

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

<强>第二

您可能会发现assertions更符合您的需求。断言是一个语句,它使用可选的消息来计算布尔结果:

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

每当断言导致false时,断言失败并抛出包含消息的AssertionError(这是一个未经检查的异常,旨在退出应用程序)。

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

答案 1 :(得分:10)

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类。

答案 2 :(得分:1)

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

我也不是完全删除调试代码的忠实粉丝。一旦投入生产,如果出现问题,您可能需要访问调试消息。如果删除所有代码级调试,则不可能。

答案 3 :(得分:0)

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

答案 4 :(得分:0)

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

答案 5 :(得分:0)

我还强烈建议使用日志框架。

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

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

你可以记录如下:

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

答案 6 :(得分:0)

这&#34; trick&#34;似乎使你的调试语句消失

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()控件应该是日志框架的工作,而不是我的工作。大多数日志记录框架都支持诸如&#34; logger&#34;,&#34; LogLevel&#34; ...这些可以解决问题的概念。

答案 7 :(得分:-2)

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

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

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

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

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