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

时间:2020-03-05 18:44:19  来源:igfitidea点击:

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

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

解决方案

回答

使用Java预处理程序? (google foo低,但这是到旧的Joel论坛上讨论的链接)

回答

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类本身删除。

回答

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

回答

直接回答问题:不知道。

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

调试语句的目的是什么?帮助消除(单元)测试中的错误。如果对某个软件进行了正确的测试,并且可以按照要求工作,那么调试语句就是过时的了。

我非常不同意将任何调试语句保留在生产代码中。我敢打赌,没有人会在生产代码中测试调试代码的副作用。该代码可能会执行应做的事情,但是它还能做更多的事情吗?我们所有的#defines是否都能正常工作,并且真的将所有调试代码都删除了?谁分析了100000行预处理代码以查看所有调试资料是否都消失了?

除非我们对生产代码有不同的定义,否则我们应该考虑在对代码进行测试并对其进行处理后取出调试语句。

回答

两个建议。

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

而且,如果我们有多个日志语句,请保护整个块:

(例如,log4j :)

if (logger.isDebugEnabled()) {

  // perform expensive operations
  // build string to log

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

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

第二:

我们可能会发现断言更多地是我们所需要的。断言是一条语句,其结果为布尔值,并带有可选消息:

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

每当断言导致错误时,断言就会失败,并引发包含消息的AssertionError(这是未经检查的异常,旨在退出应用程序)。

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

回答

我也强烈建议我们使用日志记录框架。

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

使用日志记录框架意味着我们可以在不重新启动应用程序的情况下即时配置日志记录级别。

我们可能会记录如下:

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

回答

另一种可能性是将if语句放入日志记录函数中,这样我们可以减少代码的编写,但是要以额外的函数调用为代价。

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