调试语句的最佳实践是什么,其中包含字符串操作?

我经常发现自己在log4net和log4j的调试语句中添加了连接字符串或使用字符串格式化器,我应该用“if debug”块来包围这些调试语句,以便通过处理这些参数来阻止自己浪费资源,即使调试语句将会不打印出来?

我会假设检查if(isDebug)是否比发生字符串操作更快更有效,但是当调试级别设置高于debug时,它会导致程序以不同的方式运行(更快),这可能意味着同步问题当我写入日志时,在生产中发生的事情不会发生。

对于Java,您可以尝试log5j 。

log4j的:

log.debug("This thing broke: " + foo + " due to bar: " + bar + " on this thing: " + car); 

log5j:

 log.debug("This thing broke: %s due to bar: %s on this thing: %s", foo, bar, car); log.debug("Exception #%d", aThrowable, exceptionsCount++); 

我会说这取决于调用调试语句的频率以及性能的重要性。 谨防过早优化和所有。

SLF4J常见问题解答中详细解答了这个问题 。 简而言之,使用参数化消息。 例如, entry是一个对象,你可以写:

  Object entry = new SomeObject(); logger.debug("The entry is {}.", entry); 

在评估是否记录之后,并且仅当决策是肯定的时,记录器实现将格式化消息并用条目的字符串值替换“{}”对。 换句话说,在禁用日志语句的情况下,此表单不会产生参数构造的成本。

以下两行将产生完全相同的输出。 但是,在禁用日志记录语句的情况下,第二种forms将比第一种forms的性能至少提高30倍。

  logger.debug("The new entry is "+entry+"."); logger.debug("The new entry is {}.", entry); 

你有没有测量连接这些字符串需要多少额外时间? 鉴于日志记录基础结构将获取​​结果消息,检查是否需要将它们分派到(可能)多个接收器,然后可能使用某些I / O进行写入,那么您可能无法获得任何信息。 我的感觉是,除非你的toString()机制很慢,否则这可能是一个优化太过分了。

我也对这种方法保持警惕,以防有人写这样的东西(我们知道他们不应该,但我以前见过这个)

 if (Log.isDebugEnabled()) { Log.debug("Received " + (items++) + " items"); } 

然后这将根据您的日志记录级别工作/失败。

在log4j中,建议采用以下最佳实践:

 if ( log.isDebugEnabled() ) { log.debug("my " + var + " message"; } 

这节省了字符串连接等系统资源。假设启用调试级别时程序执行速度较慢,这是正确的,但这是正确的:观察中的系统因观察而被更改。 同步问题(主要)处理线程之间的不幸时序或可变可见性,这两者都不会直接受到调试级别更改的影响。 您仍然需要与系统“一起玩”以重现multithreading问题。

我们采用了这种做法,在每个类中定义一个私有静态只读布尔值DEBUG变量。

 private static readonly log4net.ILogger LOG = log4net.LogManager.GetLogger(); private static readonly bool DEBUG = LOG.IsDebugEnabled; 

每个实际的调试日志行都是这样的

 if (DEBUG) LOG.Debug(...); 

其中……可以具有任意复杂性,仅在需要调试时进行评估。

请参阅: http : //logging.apache.org/log4net/release/faq.html ,答案为“什么是真正最快的(非)记录方式?”

这对我们有用,因为我们只在启动时读取日志配置。

由于每个函数调用至少有一个调试语句,我们觉得必须写if(DEBUG)值得努力在关闭debuging时获得最大性能。 我们在调试开启和关闭的情况下进行了一些测量,发现性能提高了10%到20%。 我们还没有测量if(DEBUG)的效果。

顺便说一句:我们只为调试消息执行此操作。 警告,信息和错误直接通过LOG.Warn等生成。

试试slf4j( http://www.slf4j.org/ )。 你写的语句如下:

 log.fine("Foo completed operation {} on widget {}", operation, widget); 

在确定日志级别足够高之前,日志消息未在库内组装。 我觉得这是最好的答案。

(这与上面的log5j解决方案非常相似。)

使用C#,我们已经开始使用委托来处理昂贵的日志语句。 只有在日志级别足够高时才会调用它:

 log.Debug(()=> "Getting the value " + SomeValue() " is expensive!"); 

这可以防止在检查的级别与记录的级别不同的日志记录错误,即:

 if(log.Level == Level.Info) log.Debug("Getting the value " + SomeValue() " is expensive!"); 

而且我觉得它更具可读性。

[编辑]如果因为不适用于Log4Net而被低估 – 那么在Log4Net周围写一个包装器来做这件事是微不足道的

条件编译与最终常量一起使用,最终常量是静态最终变量。 类可以像这样定义常量:

 private static final boolean DEBUG = false; 

定义了这样的常量,其中的任何代码都是:

 if (DEBUG) { // code } 

实际上并没有编译到类文件中 。 要激活类的调试,只需要将常量的值更改为true并重新编译该类(您可以使用两个版本的二进制文件,一个用于开发,一个用于生产)。

然而,由于几个原因,这种解决方案是次优的。

我倾向于在isDebug语句中包含所有调用调用。 我不认为这是一个不成熟的优化,这只是一个好习惯。

担心应用程序以不同的速度运行并不是真的合理,因为处理器上的负载可能/将影响您的应用程序,而不是调试代码。

从log4j配置文件中查找外化日志级别。 这样您就可以根据您的环境选择打开或关闭日志(并避免所有这些字符串连接)。

 if(log.isDebugEnabled()) { log.debug("Debug message"); }