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

问题描述 投票:0回答:8

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

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

java debugging
8个回答
24
投票

两个推荐。

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

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

(log4j,例如:)

if (logger.isDebugEnabled()) {

  // perform expensive operations
  // build string to log

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

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

第二:

您可能会发现断言更符合您的需要。断言是一个计算结果为布尔结果的语句,带有可选消息:

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

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

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


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


1
投票

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

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


1
投票

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

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”之类的概念......这可以做到这一点。


0
投票

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


0
投票

Java 包含某种自己的预处理器。它被称为APT。它处理并生成代码。目前我不确定这应该如何工作(我还没有尝试过)。不过好像是用来做这种事的。


0
投票

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

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

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

您可以进行如下记录:

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

-2
投票

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

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

调试语句的目的是什么?帮助消除(单元)测试时的错误。如果一个软件经过正确测试并按照要求工作,那么调试语句就是过时的。

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

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

© www.soinside.com 2019 - 2024. All rights reserved.