Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/379.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/logging/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何使用java8slambdas改进日志记录机制_Java_Logging_Lambda_Java 8 - Fatal编程技术网

如何使用java8slambdas改进日志记录机制

如何使用java8slambdas改进日志记录机制,java,logging,lambda,java-8,Java,Logging,Lambda,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.get

如何通过不增加字符串连接的开销来改进日志记录机制

考虑以下示例:

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;
    }
}
import java.util.logging.Level;
导入java.util.logging.Logger;
公共类LoggerTest{
公共静态void main(字符串[]args){
//获取记录器
Logger log=Logger.getLogger(LoggerTest.class.getName());
//将日志级别设置为INFO(因此不会记录罚款)
log.setLevel(Level.INFO);
//此行不会记录任何内容,但会计算getValue方法
log.fine(“跟踪值:+getValue());
}
//获取具有大量字符串串联的值的示例方法
私有静态字符串getValue(){
字符串val=”“;
对于(int i=0;i<1000;i++){
val+=“foo”;
}
返回val;
}
}
日志方法
log.fine(…)
不会记录任何内容,因为日志级别设置为
INFO
。问题是,方法
getValue
无论如何都会被评估

在有大量调试语句的大型应用程序中,这是一个很大的性能问题


那么,如何解决这个问题呢?

自从Java8以来,就有可能在这个场景中使用新引入的

以下是日志记录的修改示例:

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;
    }
}
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();
            }
        }
    }
}
import java.util.logging.Level;
导入java.util.logging.Logger;
公共类LoggerTest{
公共静态void main(字符串[]args){
//拥有自己的lambda记录器
LambdaLogger log=新的LambdaLogger(LoggerTest.class.getName());
//将日志级别设置为INFO(因此不会记录罚款)
log.setLevel(Level.INFO);
//此行不会记录任何内容,也不会计算getValue方法!
log.fine(()->“跟踪值:+getValue());//更改为lambda表达式
}
//获取具有大量字符串串联的值的示例方法
私有静态字符串getValue(){
字符串val=”“;
对于(int i=0;i<1000;i++){
val+=“foo”;
}
返回val;
}
}
LambdaLogger.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;
    }
}
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();
            }
        }
    }
}
import java.util.concurrent.Callable;
导入java.util.logging.Level;
导入java.util.logging.Logger;
公共类LambdaLogger扩展记录器{
公共LambdaLogger(字符串名称){
super(名称,空);
}
公共无效罚款(可调用消息){
//仅记录,如果可记录
if(IsLogable(标高精细)){
试一试{
//在这里计算可调用的方法
super.fine(message.call());
}捕获(例外e){
e、 printStackTrace();
}
}
}
}
通过这种修改,如果您有许多只用于调试目的的日志语句,那么您可以大大提高应用程序的性能


当然,您可以使用任何您想要的记录器。这只是bobbel解释过的
java.util.Logger

的一个例子

我想补充一点,虽然这代表了对原始代码的性能改进,但处理此问题的经典方法仍然更快:

if (log.isLoggable(Level.FINE)) {
    log.fine("Trace value: " + getValue());
}
只是稍微多了点冗长/罗嗦

它之所以更快,是因为lambda版本在创建可调用实例时有额外的运行时开销(捕获成本),以及额外级别的方法调用


最后,还有创建
LambdaLogger
实例的问题@bobbel的代码显示这是使用构造函数完成的,但实际上,
java.util.logging.Logger
对象需要通过工厂方法创建,以避免对象的扩散。这意味着需要一系列额外的基础结构(和代码更改)才能使其与
记录器的自定义子类一起工作
使用格式
字符串
,以及
供应商的数组
。这样,除非日志记录实际上是可发布的,否则不会调用
toString
方法。这样,您就不必担心有关登录应用程序代码的丑陋的
if
语句

只需为当前记录器创建包装器方法,如下所示:

public static void info(Logger logger, Supplier<String> message) {
    if (logger.isLoggable(Level.INFO))
    logger.info(message.get());
}

参考资料:JAVA SE 8,第48-49页。显然,Log4j 2.4包含了对lambda表达式的支持,这些表达式对您的案例非常有用(其他答案已手动复制):


大概如果需要将数据从调用方法传递到
Callable.call
,则可能会产生捕获成本。这可能比字符串连接更好,但仍可能比将
fine
调用包装在检查级别是否启用的if语句中更昂贵。讨论该视频中的捕获成本(跳过所有精彩的背景信息)的一个良好起点是[36:25][1]。[1] :无需扩展Logger类并实现自定义方法,因为Java 8已经有了具有类似签名的Logger方法:
public void fine(Supplier msgSupplier)
。当然,这也是一个选项(可能更快)!但是,假设您必须为每个日志语句编写这个额外的
if
。因此,如果您没有太多的日志语句,应该首选此选项@波贝尔-对不起。不,我不喜欢。我不认为有必要“保护”大多数日志调用,或者通常您的代码中首先应该有那么多“精细”的日志调用。另一点是保持
if
guard和实际log语句的级别同步。@René-True。但是这两段代码通常在连续的行上,两个日志级别符号在所有大写字母中。程序员必须非常粗心,才能使它们不同步。使用?谢谢你提供的信息!但是,如果我们说“代码> log .罚款”(“跟踪值:{}”,GETV)