C# 对于包含字符串操作的调试语句,最佳实践是什么?

C# 对于包含字符串操作的调试语句,最佳实践是什么?,c#,java,debugging,log4net,log4j,C#,Java,Debugging,Log4net,Log4j,我经常发现自己在log4net和log4j中的调试语句中添加了concatonated字符串或使用字符串格式化程序。我是否应该在这些调试语句周围加一个“if debug”块来阻止自己通过处理这些参数来浪费资源,即使调试语句不会被打印出来 我假设检查(isDebug)是否比执行字符串操作更快、更有效,但是当调试级别设置为高于调试级别时,它会导致程序以不同的方式(更快)运行,这可能意味着生产中发生的同步问题在我写入日志时不会发生。我认为这取决于调用调试语句的频率以及性能的重要性。小心过早的优化和其他

我经常发现自己在log4net和log4j中的调试语句中添加了concatonated字符串或使用字符串格式化程序。我是否应该在这些调试语句周围加一个“if debug”块来阻止自己通过处理这些参数来浪费资源,即使调试语句不会被打印出来


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

我认为这取决于调用调试语句的频率以及性能的重要性。小心过早的优化和其他一切。

对于Java,您可以尝试一下

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

条件编译使用final常量,这些常量是静态final变量。类可以这样定义常量:

private static final boolean DEBUG = false;
定义了这样一个常量后,一个

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


然而,由于几个原因,这是一个次优的解决方案。我倾向于将所有调试调用封装在isDebug语句中。我不认为这是一个过早的优化,这只是一个很好的实践


担心应用程序以不同的速度运行是不明智的,因为处理器上的负载比调试代码对应用程序的影响更大。

您是否测量了连接这些字符串所花费的额外时间?假设日志记录基础设施将接收结果消息,检查它们是否需要被分派到(可能)多个接收器,然后可能使用一些I/O进行写操作,那么您可能得不到任何东西。我的感觉是,除非您的toString()机制很慢,否则这可能是一个过于优化的结果

我也对这种方法持谨慎态度,以防有人写这样的东西(我们知道他们不应该这样做,但我以前见过这种情况)

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

尝试slf4j()。你写的陈述如下:

log.fine("Foo completed operation {} on widget {}", operation, widget);
在确定日志级别足够高之前,不会在库中组装日志消息。我觉得这是最好的答案


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

在log4j中,以下是推荐的最佳实践:

if ( log.isDebugEnabled() )
{
 log.debug("my " + var + " message";
}
这节省了字符串连接等的系统资源。您的假设是正确的,即当启用调试级别时,程序可能会执行得较慢,但这是意料之中的:观察中的系统会因为处于观察中而发生更改。同步问题(主要)涉及线程之间的不吉利计时或可变可见性,这两者都不会直接受到调试级别更改的影响。您仍然需要“玩”系统来重现多线程问题。

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

log.Debug(()=> "Getting the value " + SomeValue() " is expensive!");
这可以防止在检查的级别与记录的级别不同时出现日志错误,即:

if(log.Level == Level.Info)
    log.Debug("Getting the value " + SomeValue() " is expensive!");
我发现它更具可读性


[编辑]如果这被否决为不适用于Log4Net-在Log4Net周围编写一个包装器来实现这一点是微不足道的。

从log4j配置文件中查找外部化日志级别。通过这种方式,您可以根据您的环境选择打开或关闭日志(并避免所有这些字符串约束)


这一问题的详细答案见。简而言之,使用参数化消息。例如,
entry
是一个对象,您可以编写:

 Object entry = new SomeObject(); 
 logger.debug("The entry is {}.", entry);
在评估是否记录之后,并且只有在决定是肯定的情况下,记录器实现才会格式化消息,并用条目的字符串值替换“{}”对。换句话说,在禁用log语句的情况下,此表单不会产生参数构造成本

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

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

我们采用了在每个类中定义私有静态只读布尔调试变量的实践

private static readonly log4net.ILogger LOG = log4net.LogManager.GetLogger();
private static readonly bool DEBUG = LOG.IsDebugEnabled;
每个实际的调试日志行如下所示

if (DEBUG) LOG.Debug(...);
在哪里。。。可以具有任意复杂性,并且仅在需要调试时进行评估

请参阅:,回答“什么是真正最快的(不是)日志记录方式?”

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

因为每个函数调用至少有一条调试语句,所以我们觉得在关闭调试的情况下,必须编写if(debug)来获得最大性能是值得的。我们在调试打开和关闭的情况下进行了一些测量,发现性能提高了10%到20%。我们还没有测量if(DEBUG)的效果


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

这很酷。我认为Log4net有类似的String.Format语法。我看到一个问题,我经常在调试模式下使用log4j esp的exception参数。这看起来不支持这一点。虽然我没有看过文档,但它不在示例中。检查一下这里:我一直对log4j没有提供这种能力感到恼火。我从一个日志框架切换到另一个日志框架。当你需要处理多个日志级别时,这不是有点失控了吗。e、 g.log4j的错误、警告、信息、调试等,尤其是OP特别要求的
private static readonly log4net.ILogger LOG = log4net.LogManager.GetLogger();
private static readonly bool DEBUG = LOG.IsDebugEnabled;
if (DEBUG) LOG.Debug(...);