编译器是否可以从生产代码中删除用于调试目的(例如日志记录)的语句?调试语句需要以某种方式标记,可能使用注释。
设置属性 (debug = true) 并在每个调试语句中检查它很容易,但这会降低性能。如果编译器只是让调试语句消失,那就太好了。
编译器是否可以从生产代码中删除用于调试目的(例如日志记录)的语句?调试语句需要以某种方式标记,可能使用注释。
设置属性 (debug = true) 并在每个调试语句中检查它很容易,但这会降低性能。如果编译器只是让调试语句消失,那就太好了。
两个建议。
第一: 对于真正的日志,使用现代日志包,如 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 参数(无需重新编译)。如果未启用,则开销为零。
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 类本身也会被删除。
另一种可能性是将 if 语句放在您的日志记录函数中,这样您可以获得更少的代码,但代价是一些额外的函数调用。
我也不喜欢完全删除调试代码。投入生产后,如果出现问题,您可能需要访问调试消息。如果您删除所有代码级调试,那么这是不可能的。
这个“技巧”似乎让你的调试语句消失了
public static final boolean DEBUG = false;
if (DEBUG) { //disapeared on compilation }
该帖子说它javac
足够聪明,可以检查static final boolean
并排除调试语句。(我没有亲自尝试过)
对于日志记录,我个人不喜欢看到如下代码:
if (logger.isDebugEnabled()) {
logger.debug("....");
}
realImportantWork();
日志记录的事情分散了我的注意力realImportantWork()
。对我来说正确的方法是:
logger.debug("....");
realImportantWork()
加上排除生产中所有调试消息的配置。
我的意思是logger.isDebugEnabled()
控制应该是日志框架的工作,而不是我的工作。大多数日志框架支持诸如“logger”、“LogLevel”之类的概念,这可以解决问题。
使用Java 预处理器?(google foo low 但这是一个链接到旧的 Joel 论坛讨论它)
Java 包含自己的某种预处理器。它被称为APT。它处理并生成代码。目前我不确定这应该如何工作(我没有尝试过)。但它似乎用于这类事情。
我也强烈推荐使用日志框架。
这logger.IsDebugEnabled()
不是强制性的,只是在记录之前检查系统是否处于调试级别可以更快。
使用日志框架意味着您可以即时配置日志级别,而无需重新启动应用程序。
你可以有这样的日志记录:
logger.error("Something bad happened")
logger.debug("Something bad happend with loads more detail")
直接回答你的问题:我不知道。
但这是您问题的另一种解决方案:在我看来,这里有两个相互冲突的语句:“调试语句”和“生产代码”。
调试语句的目的是什么?在(单元)测试时帮助摆脱错误。如果一个软件经过适当的测试并按照要求工作,那么调试语句就是过时的。
我强烈反对在生产代码中留下任何调试语句。我敢打赌,没有人会费心在生产代码中测试调试代码的副作用。代码可能做了它应该做的事情,但它做的还不止这些吗?您所有的#defines 是否都能正常工作并真正消除所有调试代码?谁分析了 100000 行预处理代码,看看是否所有的调试内容都没有了?
除非我们对生产代码有不同的定义,否则您应该考虑在测试代码后取出调试语句并完成它。