log4j2 无日志记录
使用Log4j 1.x并希望避免在某些情况下可能会造成额外的性能影响(即使实际上未记录该消息)时,通常使用日志记录防护 。 Java的简单日志记录外观 ( SLF4J )带给Java日志记录的最吸引人的功能之一是能够减少需要进行这些日志级别检查的情况的数量 。 在本文中,我将探讨如何使用Log4j 2.x的日志记录API更改来实现类似的好处。
下一个代码清单演示了记录长时间运行的操作。 第一个示例在名称为“ slow”的实例上隐式调用toString()
方法。 第二个日志记录示例调用了一个长时间运行的方法。
传统,无人值守的测井
// Will implicitly invoke slow's toString() method
logger.debug("NORMAL: " + slow);
// Will explicitly invoke the long-running method expensiveOperation()
logger.debug(expensiveOperation());
在前面的示例中,即使实际上未执行任何日志记录,这两个日志记录操作也将花费很长时间。 先前代码清单中的日志记录语句仅在日志记录级别为DEBUG或不太明确的日志级别(例如TRACE)时才实际记录,但是即使没有任何记录,它们的昂贵操作也将运行。
在Log4j 1.x中,有两种方法可以解决此问题。 一种方法(通常是最好的方法)是尝试重新编写log语句,以便不涉及长时间运行的操作 。 如果不切实际(例如,当需要与长时间运行的操作关联的上下文以使日志消息有用时),则可以使用日志保护。 接下来演示在Log4j 1.x中有效的这种方法。
传统的,受保护的日志记录
if (logger.isDebugEnabled())
{logger.debug("GUARDED: " + slow);logger.debug(expensiveOperation());
}
如上一代码清单所示,日志防护措施可以有效地防止调用长时间运行的操作,即使无论如何也不会记录任何消息。 但是,使用日志保护确实会带来一些缺点。 也许最主要的缺点是引入了额外的(有些人会说是code肿的)代码。 另一个潜在的缺点很少见,但更为严重:由于条件块和关联块引入了额外的作用域,因此更容易在条件块中引入错误代码,甚至有可能在依赖于日志记录级别的情况下带来副作用代码块。
最常见的情况之一是,日志调用实际上不会记录任何内容,但会显着影响性能,这是当将对象传递给logger调用或与传递给该字符串的字符串连接时,显式或隐式调用对象的toString()方法。记录器调用。 在上面的两个代码清单中,通过将字符串文字“ GUARDED:”与名为“ slow”的变量的隐式调用toString()
方法进行连接,将字符串传递给logger调用,证明了这种情况。
SLF4J 普及了参数化日志记录调用的概念,Log4j 2在其日志记录API中提供了类似的支持 。 下面的代码演示了如何使用它。
参数化记录
logger.debug("PARAMETERIZED: {}", slow);
logger.debug("{}", expensiveOperation());
当上面的参数化日志记录示例以比DEBUG更特定的日志级别执行时,由于参数化日志记录,将不会尝试在“ slow”变量上使用隐式toString()
。 但是,参数化日志记录无法帮助其他日志记录情况,因为尽管进行了参数化日志记录,但仍将调用方法expensiveOperation()
。 还要注意,尽管参数化日志记录在隐式toString()
调用的情况下有所帮助 ,但在显式toString()
调用中却无济于事 。 即使日志记录级别比DEBUG更具体,在logger语句中对slow.toString()
的调用仍会导致性能slow.toString()
。
Log4j 2.4引入了一种基于Lambda的机制 ,该机制可用于延迟对传递给logger调用的方法的调用,这样,如果该语句的记录级别低于当前日志级别,则根本不需要执行它们。 。 这表现在下一代码列表,其中toString()
方法被明确地通过λ表达式称为“慢”变量的对象上,并且expensiveOperation
方法是通过调用方法的参考 。
Lambda表达式记录
logger.debug("LAMBDA: ", () -> slow.toString());
logger.debug("{}", this::expensiveOperation);
当以上代码的日志级别设置为比DEBUG更具体的级别时,由于基于lambda表达式的延迟加载,因此不会调用“慢速”对象的toString()
方法和expensiveOperation
方法。 换句话说,类似于该示例与警卫一起使用的方式,使用lambda表达式可以防止不必要地执行可能长时间运行的方法,除非它们的结果要真正记录下来。 此lambda表达式支持已在2.4版本中添加到Log4j,并且当然需要Java 8 。
摘要
Log4j 2(2.4)提供了多种方法来避免在未实际记录消息时对日志语句的性能造成影响。
- 可以重写日志语句,以便根本不记录昂贵的方法(包括昂贵的
toString()
调用)。 - 日志保护可以用来确保仅在实际记录消息时才执行log语句的长时间运行的方法调用。
- 除非确实记录了消息,否则Log4j 2的参数化(格式化)记录器API可用于消除对隐式
toString()
方法的调用。 - Log4j 2.4的lambda表达式记录器API可用于消除对已记录消息所需的任何操作(隐式或显式)的调用,除非已真正记录了该消息。
翻译自: https://www.javacodegeeks.com/2015/10/better-performing-non-logging-logger-calls-in-log4j2.html
log4j2 无日志记录