编译器是否可以从生产代码中删除用于调试目的的语句(例如日志记录)?需要以某种方式标记调试语句,可能使用注释。
设置属性(debug = true)很容易,并在每个调试语句中检查它,但这会降低性能。如果编译器只是简单地使调试语句消失,那将是很好的。
答案 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行预处理代码,看看是否所有调试内容都消失了?
除非我们对生产代码有不同的定义,否则您应该考虑在代码测试完成后取出调试语句。