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

时间:2009-05-28 11:45:54

标签: c# java debugging log4net log4j

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

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

11 个答案:

答案 0 :(得分:7)

对于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++); 

答案 1 :(得分:3)

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

答案 2 :(得分:3)

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

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

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

以下两行将产生完全相同的输出。但是,如果禁用日志记录,第二种形式的性能将比第一种形式的性能提高至少30倍。

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

答案 3 :(得分:2)

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

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

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

然后根据您的日志记录级别进行操作/失败。

答案 4 :(得分:2)

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

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

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

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

答案 5 :(得分:2)

在log4j中,建议采用以下最佳做法:

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

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

答案 6 :(得分:2)

我们已采用这种做法在每个类中定义一个私有静态只读布尔值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等生成。

答案 7 :(得分:1)

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

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

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

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

我觉得它更具可读性。

[编辑]如果因为没有应用于Log4Net而被低估 - 那么琐碎就可以在Log4Net周围编写一个包装器来执行此操作。

答案 8 :(得分:0)

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

private static final boolean DEBUG = false;

定义了这样的常量,在:

中的任何代码
if (DEBUG) {
    // code
} 

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

然而,由于多种原因,这种解决方案不是最理想的。

答案 9 :(得分:0)

我倾向于在isDebug语句中包含对debug的所有调用。我不认为这是一个过早的优化,这只是一个很好的做法。

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

答案 10 :(得分:0)

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

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