bob*_*bel 9 java lambda logging java-8
如何通过不增加字符串连接的开销来改进您的日志记录机制呢?
请考虑以下示例:
import java.util.logging.Level;
import java.util.logging.Logger;
public class LoggerTest {
public static void main(String[] args) {
// get logger
Logger log = Logger.getLogger(LoggerTest.class.getName());
// set log level to INFO (so fine will not be logged)
log.setLevel(Level.INFO);
// this line won't log anything, but will evaluate the getValue method
log.fine("Trace value: " + getValue());
}
// example method to get a value with a lot of string concatenation
private static String getValue() {
String val = "";
for (int i = 0; i < 1000; i++) {
val += "foo";
}
return val;
}
}
Run Code Online (Sandbox Code Playgroud)
log方法log.fine(...)
不会记录任何内容,因为日志级别设置为INFO
.问题是,getValue
无论如何都要评估该方法.
在具有大量调试语句的大型应用程序中,这是一个很大的性能问题.
那么,如何解决这个问题呢?
bob*_*bel 12
从Java8开始,可以在此方案中使用新引入的lambda表达式.
以下是日志记录的修改示例:
LoggerTest.class
import java.util.logging.Level;
import java.util.logging.Logger;
public class LoggerTest {
public static void main(String[] args) {
// get own lambda logger
LambdaLogger log = new LambdaLogger(LoggerTest.class.getName());
// set log level to INFO (so fine will not be logged)
log.setLevel(Level.INFO);
// this line won't log anything, and will also not evaluate the getValue method!
log.fine(()-> "Trace value: " + getValue()); // changed to lambda expression
}
// example method to get a value with a lot of string concatenation
private static String getValue() {
String val = "";
for (int i = 0; i < 1000; i++) {
val += "foo";
}
return val;
}
}
Run Code Online (Sandbox Code Playgroud)
LambdaLogger.class
import java.util.concurrent.Callable;
import java.util.logging.Level;
import java.util.logging.Logger;
public class LambdaLogger extends Logger {
public LambdaLogger(String name) {
super(name, null);
}
public void fine(Callable<String> message) {
// log only, if it's loggable
if (isLoggable(Level.FINE)) {
try {
// evaluate here the callable method
super.fine(message.call());
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果您有许多日志语句(仅用于调试目的),通过此修改,您可以大大提高应用程序的性能.
当然,您可以使用任何您想要的记录器.这只是一个例子java.util.Logger
.
@bobbel解释了如何做到这一点.
我想补充一点,虽然这代表了对原始代码的性能提升,但处理此问题的经典方法仍然更快:
if (log.isLoggable(Level.FINE)) {
log.fine("Trace value: " + getValue());
}
Run Code Online (Sandbox Code Playgroud)
并且只是略微冗长/冗长.
它更快的原因是lambda版本具有创建可调用实例(捕获成本)的额外运行时开销,以及额外级别的方法调用.
最后,还有创建LambdaLogger
实例的问题.@ bobbel的代码显示这是使用构造函数完成的,但实际上java.util.logging.Logger
对象需要通过工厂方法创建,以避免对象的扩散.这意味着需要一大堆额外的基础设施(和代码更改)才能使其与自定义子类一起使用Logger
.
显然,Log4j 2.4包含对lambda表达式的支持,这些表达式对您的情况非常有用(以及其他答案已手动复制):
// Uses Java 8 lambdas to build arguments on demand
logger.debug("I am logging that {} happened.", () -> compute());
Run Code Online (Sandbox Code Playgroud)
只需为当前记录器创建包装器方法:
public static void info(Logger logger, Supplier<String> message) {
if (logger.isLoggable(Level.INFO))
logger.info(message.get());
}
Run Code Online (Sandbox Code Playgroud)
并使用它:
info(log, () -> "x: " + x + ", y: " + y);
Run Code Online (Sandbox Code Playgroud)
参考:JAVA SE 8 for the Really Impatient eBook,第48-49页.
归档时间: |
|
查看次数: |
13593 次 |
最近记录: |