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

时间:2023-01-20 22:44:38

I often find myself adding either concatonated strings or using a string formatter in my debug statements in log4net and log4j should I surround these debug statements with an "if debug" block to stop myself from wasting resources by processing these parameters even though the debug statement will not be printed out?

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

I would assume that checking if (isDebug) would be quicker and more efficient than having the string operations occuring, however it would lead to the program operating differently (faster) when the debug level is set higher than debug, which could mean that synchronisation problems that happen in production don't happen when I'm writing to the log.

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

11 个解决方案

#1


for Java you can try log5j.

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

#2


I'd say it depends on how often the debug statement is called and how important performance is. Beware of premature optimization and all.

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

#3


This question is answered in detail in the SLF4J FAQ. In short, use parameterized messages. For example, entry is an object, you can write:

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

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

After evaluating whether to log or not, and only if the decision is affirmative, will the logger implementation format the message and replace the '{}' pair with the string value of entry. In other words, this form does not incur the cost of parameter construction in case the log statement is disabled.

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

The following two lines will yield the exact same output. However, the second form will outperform the first form by a factor of at least 30, in case of a disabled logging statement.

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

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

#4


Have you measured how much extra time is taken concatenating these strings ? Given that the logging infrastructure will take the resultant messages, check whether they need to be dispatched to (possibly) multiple sinks, and then possibly write using some I/O, then you may not be gaining anything. My feeling is that unless your toString() mechanism is slow, this may be an optimisation too far.

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

I'm also wary of this approach in case someone writes something like this (and we know they shouldn't but I've seen this before)

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

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

and this will then work/fail depending on your logging level.

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

#5


In log4j the following is recommended best practice:

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

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

This saves on system resources from the string concatenation etc. You are correct in your assumption that the program may perform slower when debug level is enabled, but that is to be expected: A system under observation is changed because it is under observation. Synchronization issues deal (mostly) with unlucky timing or variable visibility between threads, both of which will not be directly affected by an alteration of the debug level. You will still need to "play" with the system to reproduce multi-threaded problems.

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

#6


We have adopted the practice to define a private static readonly boolean DEBUG variable in each class.

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

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

each actual debug log line looks like this

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

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

where ... can have arbitrary complexity and is only evaluated when debugging is required.

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

See: http://logging.apache.org/log4net/release/faq.html, the answer to "What is REALLY the FASTEST way of (not) logging?"

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

This works for us since we only read the log config at startup.

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

Since we have at least one debug statement per function call, we felt that having to write if (DEBUG) was worth the effort to get maximum performance with debuging switched off. We did some measurements with debugging switched on and off and found performance increases between 10 and 20%. We haven't measured what the effect of the if (DEBUG) was.

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

By the way: we only do this for debug messages. Warnings, informationals and errors are produced directly via LOG.Warn, etc.

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

#7


Try slf4j (http://www.slf4j.org/). You write statements like:

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

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

The log message is not assembled inside the library until the log level has been determined to be high enough. I find this the best answer.

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

(This is a lot like the log5j solution above.)

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

#8


With C#, we've started using delegates for expensive log statements. It's only called if the log level is high enough:

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

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

This prevents logging bugs where the level checked for is different to the level logged at, ie:

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

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

and I find it much more readable.

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

[Edit] If that was downvoted for not applying to Log4Net - it is trivial to write a wrapper around Log4Net to do this.

[编辑]如果因为不适用于Log4Net而被低估 - 那么在Log4Net周围编写一个包装器就可以了。

#9


Conditional compilation works with final constants, which are static final variables. A class might define the constant like this:

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

private static final boolean DEBUG = false;

With such a constant defined, any code within an:

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

if (DEBUG) {
    // code
} 

is not actually compiled into the class file. To activate debugging for the class, it is only necessary to change the value of the constant to true and recompile the class (you can have two version of your binaries, one for development and one for production).

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

However this is solution is suboptimal for several reasons.

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

#10


I tend to wrap all calls to debug in an isDebug statement. I don't think that's a premature optimization, it's just good practice.

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

Worrying about the app running at different speeds isn't really sensible, as the load on the processor can / will influence your app more then the debugging code.

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

#11


Look for the externalized log level from log4j config file. That way you have the option to switch on or off logs based on your environment (and avoid all those string concatinations).

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

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

#1


for Java you can try log5j.

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

#2


I'd say it depends on how often the debug statement is called and how important performance is. Beware of premature optimization and all.

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

#3


This question is answered in detail in the SLF4J FAQ. In short, use parameterized messages. For example, entry is an object, you can write:

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

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

After evaluating whether to log or not, and only if the decision is affirmative, will the logger implementation format the message and replace the '{}' pair with the string value of entry. In other words, this form does not incur the cost of parameter construction in case the log statement is disabled.

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

The following two lines will yield the exact same output. However, the second form will outperform the first form by a factor of at least 30, in case of a disabled logging statement.

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

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

#4


Have you measured how much extra time is taken concatenating these strings ? Given that the logging infrastructure will take the resultant messages, check whether they need to be dispatched to (possibly) multiple sinks, and then possibly write using some I/O, then you may not be gaining anything. My feeling is that unless your toString() mechanism is slow, this may be an optimisation too far.

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

I'm also wary of this approach in case someone writes something like this (and we know they shouldn't but I've seen this before)

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

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

and this will then work/fail depending on your logging level.

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

#5


In log4j the following is recommended best practice:

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

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

This saves on system resources from the string concatenation etc. You are correct in your assumption that the program may perform slower when debug level is enabled, but that is to be expected: A system under observation is changed because it is under observation. Synchronization issues deal (mostly) with unlucky timing or variable visibility between threads, both of which will not be directly affected by an alteration of the debug level. You will still need to "play" with the system to reproduce multi-threaded problems.

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

#6


We have adopted the practice to define a private static readonly boolean DEBUG variable in each class.

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

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

each actual debug log line looks like this

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

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

where ... can have arbitrary complexity and is only evaluated when debugging is required.

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

See: http://logging.apache.org/log4net/release/faq.html, the answer to "What is REALLY the FASTEST way of (not) logging?"

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

This works for us since we only read the log config at startup.

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

Since we have at least one debug statement per function call, we felt that having to write if (DEBUG) was worth the effort to get maximum performance with debuging switched off. We did some measurements with debugging switched on and off and found performance increases between 10 and 20%. We haven't measured what the effect of the if (DEBUG) was.

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

By the way: we only do this for debug messages. Warnings, informationals and errors are produced directly via LOG.Warn, etc.

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

#7


Try slf4j (http://www.slf4j.org/). You write statements like:

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

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

The log message is not assembled inside the library until the log level has been determined to be high enough. I find this the best answer.

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

(This is a lot like the log5j solution above.)

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

#8


With C#, we've started using delegates for expensive log statements. It's only called if the log level is high enough:

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

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

This prevents logging bugs where the level checked for is different to the level logged at, ie:

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

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

and I find it much more readable.

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

[Edit] If that was downvoted for not applying to Log4Net - it is trivial to write a wrapper around Log4Net to do this.

[编辑]如果因为不适用于Log4Net而被低估 - 那么在Log4Net周围编写一个包装器就可以了。

#9


Conditional compilation works with final constants, which are static final variables. A class might define the constant like this:

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

private static final boolean DEBUG = false;

With such a constant defined, any code within an:

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

if (DEBUG) {
    // code
} 

is not actually compiled into the class file. To activate debugging for the class, it is only necessary to change the value of the constant to true and recompile the class (you can have two version of your binaries, one for development and one for production).

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

However this is solution is suboptimal for several reasons.

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

#10


I tend to wrap all calls to debug in an isDebug statement. I don't think that's a premature optimization, it's just good practice.

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

Worrying about the app running at different speeds isn't really sensible, as the load on the processor can / will influence your app more then the debugging code.

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

#11


Look for the externalized log level from log4j config file. That way you have the option to switch on or off logs based on your environment (and avoid all those string concatinations).

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

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