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

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

设置属性(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(这是一个未经检查的exception,旨在退出应用程序)。

整洁的是,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语句放在日志记录函数中,以这种方式获得更少的代码,但代价是一些额外的函数调用。

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

使用Java预处理器 ? (google foo low但是这是一个链接到老Joel论坛讨论它)

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

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

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

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

你可以像以下一样记录:

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

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

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

post说javac非常聪明,可以检查static final boolean并排除调试语句。 (我没亲自尝试过)

对于日志记录,我个人不喜欢看到如下代码:

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

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

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

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

我的意思是logger.isDebugEnabled()控件应该是日志框架的工作,而不是我的工作。 大多数日志记录框架支持“logger”,“LogLevel”等概念,可以解决这个问题。

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

但是这里是你问题的另一个解决方案:在我看来,这里有两个相互冲突的语句:“debug statements”和“production code”。

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

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

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