简体   繁体   English

懒惰评估登录 Java 8

[英]Lazy evaluation for logging in Java 8

When you have values than are expensive to compute, a common pattern you see in logging frameworks is当您的值比计算成本高时,您在日志框架中看到的常见模式是

if (log.isDebugEnabled()) {
    String value = expensiveComputation();
    log.debug("value: {}", value);
}

Since Java 8 added lambdas, it'd be nice to do:由于 Java 添加了 8 个 lambda,所以这样做会很好:

log.debug("value: {}", (Supplier<String>) this::expensiveComputation);

Which almost works because the logging framework will do toString() on the parameter.几乎可以工作,因为日志记录框架将对参数执行toString() The problem is toString() on Supplier is the implementation in Object .问题是Supplier上的toString()Object中的实现。

Is there a way to supply something that's evaluated lazily to Logger methods?有没有办法向Logger方法提供惰性评估的内容? It would almost just be a Supplier with a default toString() that calls get() .它几乎只是一个具有调用get()的默认toString()Supplier

To pass an argument that will executed in a lazy way the String computation, you have to pass a Supplier and not a String . 要传递将以惰性方式执行String计算的参数,您必须传递Supplier而不是String
The method that you invoke should have this signature : 您调用的方法应具有以下签名:

void debug(Supplier<?> msgSupplier, Throwable t)

You could introduce this utility method in your own utility class. 您可以在自己的实用程序类中引入此实用程序方法。
But you should not need to do that as recent logging frameworks such as Log4j2 provides this feature out of the box. 但是您不需要这样做,因为最近的日志记录框架(如Log4j2)提供了开箱即用的功能。

For example, org.apache.logging.log4j.Logger provides overloaded methods to log that accept a Supplier . 例如, org.apache.logging.log4j.Logger提供了重载方法来记录接受Supplier
For example : 例如

void debug(MessageSupplier msgSupplier, Throwable t)

Logs a message (only to be constructed if the logging level is the DEBUG level) including the stack trace of the Throwable t passed as parameter. 记录消息(仅在日志记录级别为DEBUG级别时构造),包括作为参数传递的Throwable t的堆栈跟踪。 The MessageSupplier may or may not use the MessageFactory to construct the Message. MessageSupplier可能会也可能不会使用MessageFactory来构造Message。

Parameters : Parameters

msgSupplier - A function, which when called, produces the desired log message. msgSupplier - 一个函数,在调用时会生成所需的日志消息。

t - the exception to log, including its stack trace. t - 记录的异常,包括其堆栈跟踪。

From Log4j2 documentation : 从Log4j2文档:

Java 8 lambda support for lazy logging Java 8 lambda支持延迟日志记录

In release 2.4, the Logger interface added support for lambda expressions. 在2.4版中,Logger接口添加了对lambda表达式的支持。 This allows client code to lazily log messages without explicitly checking if the requested log level is enabled. 这允许客户端代码懒惰地记录消息,而无需显式检查是否启用了请求的日志级别。 For example, previously you would write: 例如,以前你会写:

 if (logger.isTraceEnabled()) { logger.trace("Some long-running operation returned {}", expensiveOperation()); } 

With Java 8 you can achieve the same effect with a lambda expression. 使用Java 8,您可以使用lambda表达式实现相同的效果。 You no longer need to explicitly check the log level: 您不再需要显式检查日志级别:

 logger.trace("Some long-running operation returned {}", () -> expensiveOperation()); 

A small helper object will allow you to do what you want: 一个小帮手对象将允许您做你想要的:

public class MessageSupplier {
    private Supplier<?> supplier;

    public MessageSupplier(Supplier<?> supplier) {
        this.supplier = supplier;
    }

    @Override
    public String toString() {
        return supplier.get().toString();
    }

    public static MessageSupplier msg(Supplier<?> supplier) {
        return new MessageSupplier(supplier);
    }
}

Then, with a static import of msg : 然后,使用静态导入的msg

log.debug("foo: {}", msg(this::expensiveComputation));

Which almost works because the logging framework will do toString() on the parameter. 几乎起作用,因为日志框架将对参数执行toString()

This statement isn't correct. 这种说法不正确。 If you step into the debug / info /whatever method, you'll find this implementation: 如果你进入debug / info / whatever方法,你会发现这个实现:

public void log(Level level, Supplier<String> msgSupplier) {
    if (!isLoggable(level)) {
        return;
    }
    LogRecord lr = new LogRecord(level, msgSupplier.get());
    doLog(lr);
}

If the level isn't met, the Supplier isn't even used. 如果未达到该level ,则甚至不使用Supplier

Interestingly you can't even use something like this 有趣的是,你甚至不能使用这样的东西

interface LazyString { String toString(); }

as a functional interface 作为功​​能界面

The only way I found so far is via anonymous classes. 到目前为止我找到的唯一方法是通过匿名课程。

Object o = new Object() { @Override public String toString() { return myExpensiveComputation(); } }; System.out.printf("%s", o);

For java.util.logging and Java 8+ you can also use this lazy and handy notation: 对于java.util.logging和Java 8+,您还可以使用这种懒惰且方便的表示法:

LOGGER.fine(() -> "Message1: "  + longComputation1() + ". Message2: " + longComputation2());

longComputation1() and longComputation2() will be called lazy - ie only when needed. longComputation1()longComputation2()将被称为lazy - 即仅在需要时。

Since you revealed you use SLF4J which still does not support this at the core, this might come in handy if you code in Kotlin (which you can with JDK 8):因为你透露你使用的 SLF4J 仍然不支持它的核心,如果你在 Kotlin 中编码(你可以使用 JDK 8),这可能会派上用场:

fun Logger.info( provider: () -> String ) { if (this.isInfoEnabled) this.info(provider.invoke())
fun Logger.debug( provider: () -> String ) { if (this.isDebugEnabled) this.debug(provider.invoke())
fun Logger.trace( provider: () -> String ) { if (this.isTraceEnabled) this.trace(provider.invoke()) 

Usage:用法:

log.trace { "State dump: " + expensiveLongSerialisation(state) }

Source: My Gist:) https://gist.github.com/OndraZizka/a7381b8cd86f734bc3b6bf9e528a01ad资料来源:我的要点:) https://gist.github.com/OndraZizka/a7381b8cd86f734bc3b6bf9e528a01ad

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM