如何使用 Java8s lambdas 改进日志记录机制
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21751954/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to improve logging mechanism with Java8s lambdas
提问by bobbel
How is it possible, to improve your logging mechanism, by not having the overhead of string concatenations?
如何通过没有字符串连接的开销来改进您的日志记录机制?
Consider the following example:
考虑以下示例:
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;
}
}
The log method log.fine(...)
will not log anything, because the log level is set to INFO
. The problem is, that the method getValue
will be evaluated anyway.
log 方法log.fine(...)
不会记录任何内容,因为日志级别设置为INFO
. 问题是,getValue
无论如何都会评估该方法。
And this is a big performance issue in big applications with a lot of debug statements.
在具有大量调试语句的大型应用程序中,这是一个很大的性能问题。
So, how to solve this problem?
那么,如何解决这个问题呢?
回答by bobbel
Since Java8 it is possible to use the new introduced lambda expressionsfor this scenario.
从 Java8 开始,可以在这种情况下使用新引入的lambda 表达式。
Here is a modified example of the logging:
这是日志记录的修改示例:
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;
}
}
LambdaLogger.class
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();
}
}
}
}
With this modification you can improve the performance of your applications a lot, if you have many log statements, which are only for debugging purposes.
如果您有许多仅用于调试目的的日志语句,则通过此修改可以大大提高应用程序的性能。
Of course you can use any Logger you want. This is only an example of the java.util.Logger
.
当然,您可以使用任何您想要的 Logger。这只是一个例子java.util.Logger
。
回答by Stephen C
@bobbel has explained how to do it.
@bobbel 已经解释了如何去做。
I'd like to add that while this represents a performance improvement over your original code, the classic way of dealing with this is still faster:
我想补充一点,虽然这代表了对原始代码的性能改进,但处理此问题的经典方法仍然更快:
if (log.isLoggable(Level.FINE)) {
log.fine("Trace value: " + getValue());
}
and only marginally more verbose / wordy.
并且只是稍微冗长/罗嗦。
The reason it is faster is that the lambda version has the additional runtime overheads of creating the callable instance (capture cost), and an extra level of method calls.
它更快的原因是 lambda 版本具有创建可调用实例的额外运行时开销(捕获成本),以及额外的方法调用级别。
And finally, there is the issue of creating the LambdaLogger
instances. @bobbel's code shows this being done using a constructor, but in reality java.util.logging.Logger
objects need to be created by a factory method to avoid proliferation of objects. That implies a bunch of extra infrastructure (and code changes) to get this to work with a custom subclass of Logger
.
最后,还有创建LambdaLogger
实例的问题。@bobbel 的代码显示这是使用构造函数完成的,但实际上java.util.logging.Logger
需要通过工厂方法创建对象以避免对象扩散。这意味着需要大量额外的基础设施(和代码更改)才能使其与Logger
.
回答by aepurniet
use a format String
, and an array of Supplier<String>
. this way no toString
methods are called unless the the log record is actually publishable. this way you dont have to bother with ugly if
statements about logging in application code.
使用格式String
和一个数组Supplier<String>
。这样,toString
除非日志记录实际上是可发布的,否则不会调用任何方法。这样您就不必为if
有关登录应用程序代码的丑陋语句而烦恼。
回答by mauretto
Just create wrapper methods for your current logger as:
只需为您当前的记录器创建包装方法:
public static void info(Logger logger, Supplier<String> message) {
if (logger.isLoggable(Level.INFO))
logger.info(message.get());
}
and use it:
并使用它:
info(log, () -> "x: " + x + ", y: " + y);
Reference: JAVA SE 8 for the Really Impatient eBook, pages 48-49.
参考:JAVA SE 8 的真正不耐烦的电子书,第 48-49 页。
回答by jhyot
Apparently Log4j 2.4 includes support for lambda expressions which are exactly useful for your case (and which other answers have replicated manually):
显然,Log4j 2.4 包括对 lambda 表达式的支持,这对您的案例非常有用(以及其他手动复制的答案):
// Uses Java 8 lambdas to build arguments on demand
logger.debug("I am logging that {} happened.", () -> compute());