【问题标题】:Is there a need to do a if(log.isDebugEnabled()) { ... } check? [duplicate]是否需要进行 if(log.isDebugEnabled()) { ... } 检查? [复制]
【发布时间】:2011-09-24 04:01:24
【问题描述】:

是否需要进行明确的 if(log.isDebugEnabled()) { ... } 检查?

我的意思是我看到一些帖子提到 log.debug("something") 在进行日志记录之前会进行隐式调用以查看是否已启用调试模式日志记录。我是否遗漏了什么,或者在使用它之前是否需要执行中间步骤?

谢谢!

log.debug("ResultSet rs is retrieved from OracleTypes");

if(log.isDebugEnabled()){
     log.debug("ResultSet rs is retrieved from OracleTypes");
}

编辑: 写过这个: http://java.sg/whether-to-do-a-isdebugenabled-checking-before-printing-out-your-log-statement/

【问题讨论】:

  • 出于好奇:您是否有理由不将那篇文章作为答案发布?这将是 SO 上最首选的方式。

标签: java if-statement log4j logging


【解决方案1】:

我通过在我的代码中执行检查与不执行检查来检查以下代码。有趣的是,如果在我们的代码中对执行一百万次的 4 日志语句执行检查,则需要额外花费 400 毫秒。我正在使用 SLF4J 1.6.6。如果您可以承受每百万请求 400 毫秒的延迟,则不需要检查。

    long startTime = System.currentTimeMillis();
    for (int i = 0; i < 1000000; i++) {
        if (logger.isTraceEnabled()) {
            logger.trace(request.getUserID());
            logger.trace(request.getEntitlementResource().getResourceString());
            logger.trace(request.getEntitlementResource().getActionString());
            logger.trace(request.getContextMap());
        }
    }
    long endTime = System.currentTimeMillis();
    logger.fatal("With Check Enabled : " + (endTime - startTime) + " ms");

    startTime = System.currentTimeMillis();
    for (int i = 0; i < 1000000; i++) {

        logger.trace(request.getUserID());
        logger.trace(request.getEntitlementResource().getResourceString());
        logger.trace(request.getEntitlementResource().getActionString());
        logger.trace(request.getContextMap());

    }
    endTime = System.currentTimeMillis();
    logger.fatal("With Check Disabled : " + (endTime - startTime)  + " ms");

---输出---

*2016-01-07 10:49:11,501 错误 [:http-bio-8080-exec-3] [com.citi.cmb.entitlement.service.EntitlementServiceImpl][]- 启用检查: 661 毫秒

2016-01-07 10:49:57,141 错误 [:http-bio-8080-exec-3] [com.citi.cmb.entitlement.service.EntitlementServiceImpl][]- 禁用检查:1043毫秒

【讨论】:

  • 在第一个块中,isTraceEnabled 仅针对 4 个语句检查一次,实际上情况并非如此,因为将检查每个语句 isTraceEnabled。如果您可以发布此更改的结果,那就太好了。
  • 最好比较字符串concat,因为我们不知道request是如何工作的。
  • 它只是一个吸气剂。没有其他逻辑
【解决方案2】:

我知道这是旧的,但对于任何刚刚发现它的人......

如果您使用 SLF4J,则可以通过使用消息格式来避免 isDebugEnabled() 调用。

例如,而不是:

Object entry = new SomeObject();
logger.debug("The entry is " + entry + ".");

用途:

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

除非启用调试,否则不会评估消息格式。

因此,对于简单的情况,您可以避免使用 isDebugEnabled()。

但在构建其中一个参数可能很昂贵的情况下,您仍然希望使用 isDebugEnabled()(即使使用 SLF4J)。

例如:

if (logger.isDebugEnabled()) {
    logger.debug("Here is the SQL: {}", sqlWrapper.buildSQL());  // assume buildSQL() is an expensive operation
}

在这种情况下,除非实际启用了调试,否则您不想评估 buildSQL()。

对于 SLF4J,始终使用它还是有选择地使用它存在一些争论。这真的归结为个人喜好。您可能想在任何地方使用,以防止其他开发人员(在不知不觉中)将您的日志消息更改为将来更复杂/更昂贵的内容。

【讨论】:

  • 你举了一个很好的例子,为了优化,检查操作(isDebugEnabled)派上用场了。其他人认为,如果您想确保调试消息不会独立于logger.debug 方法内部实现而记录...我的意思是,现在...你们说这个方法在内部检查调试已启用(Logger 类的 javadoc 也这么说)......但是如果(不应该发生)记录器的实现者更改合同(从后面的刀)会发生什么,即使在这种情况下你的日志也不会' t 记录该信息
  • 我不认为这个说法是正确的但是在构建其中一个参数可能很昂贵的情况下,您仍然希望使用 isDebugEnabled()(即使使用 SLF4J)。 那么使用{}没有意义
  • 其实你错了。这是来自 SLF4J: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.
  • @yngwietiger 你是对的!该方法仍会被调用:logger.debug("Hello World {}", test()); 将调用 test() 而不管记录器级别如何
  • @yngwietiger 哦,等等,您概述的场景 - 在日志消息中调用方法并不是真正的正常场景。是的,该方法将被调用,但对象不会对其 toString() 进行评估。这与文档所说的一致:logger.debug("Hello World {}", test()); test 将被调用,但logger.debug("Hello World {}", test); 其中test 是一个对象将不会调用其toString()。所以,是的
【解决方案3】:

声明:

if(log.isDebugEnabled()){

仅出于性能原因使用。它的使用是可选的,因为它是由内部的 log 方法调用的。

但是现在你问这个检查是否是内部进行的,那我为什么要使用它呢? 这很简单:如果你记录一些像这样简单的东西:

log.debug("ResultSet rs is retrieved from OracleTypes");

那么你不需要做任何检查。如果您像这样使用附加运算符 (+) 编写要记录的字符串:

log.debug("[" + System.getTimeInMillis() + "] ResultSet rs is retrieved from OracleTypes");

在这种情况下,您应该检查是否启用了日志,因为如果没有,即使没有创建日志,字符串组合也是。而且我必须提醒你,使用运算符“+”来连接字符串是非常低效的。

【讨论】:

  • +1 提到在要调试的内容上已经完成了工作,然后再检查是否需要打印日志语句,我会正确解释它吗?
  • 字符串连接在内部会创建很多临时对象。如果你有很多这样的日志语句,即使日志被禁用,你也会失去性能。
  • 取决于字符串 - 如果字符串不是动态创建的,那么它们很有可能被缓存在字符串池中。无论如何,我会按照你自己的建议去做 - 不太可能总是记录相同的字符串。
  • 由于我们要添加一个分支,我想知道分支错误预测/推测执行是否会在这里伤害我们,特别是当我们添加到字符串的值同样可能被缓存时(一个对象id,例如)和 log.isDebugEnabled() 一样 - 在这种情况下,无论如何都可能开始添加,并且获取相对便宜。
  • 很好,我只是给出我的 5c 意见:除非性能原因真的很关键(因此你应该问自己为什么使用 Java)我的意见是避免检查,因为它会产生很多的代码噪声。可读性分数通常比几毫秒的性能付出更多
【解决方案4】:

最新版本的 Logger 简化了这一点,因此没有太大区别。

最大的不同是您不必创建要记录的内容 - 有时会添加很多字符串。

【讨论】:

  • 您是否会知道 Logger 从哪个版本开始简化此功能?
  • 我想这里的问题不是记录器如何打印它,而是在 logger.debug 去检查是否需要打印之前,logger.debug 方法中的表达式(参数)被评估.无论是否启用调试,这都是一些计算。我不认为日志框架可以阻止这种情况的发生。 @yngwietiger 解释得很好
  • @raja 如果您先检查,则不会评估要记录的表达式。更好的方法是将供应商传递给记录方法,该方法允许按需延迟计算。
  • 您可以推荐任何特定版本或风格的日志框架吗?
  • @raja 不是真的。登录java仍然没有明确的赢家。我倾向于使用与容器/平台捆绑在一起的任何东西。
【解决方案5】:

这样做的原因是出于性能原因。如果首先检查这一点,则不应评估 log.debug(... 语句。

在功能上确实是一样的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-06
    • 1970-01-01
    • 2021-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-11
    相关资源
    最近更新 更多