5.7 利用Supplier创建日志消息
问题
用户希望创建由日志级别(log level)控制是否可见的日志消息。
方案
使用 java.util.logging.Logger 类新增的各种日志方法,它们传入 Supplier 作为参数。
讨论
目前,Logger 类中的日志方法(如 info、warning、severe 等)包括两种重载形式,一种传入单个 String 作为参数,另一种传入 Supplier 作为参数。
例 5-20 显示了各种日志方法的签名。7
7读者或许奇怪,Java 日志框架的设计者为什么不采用与其他日志 API 相同的日志级别(Trace、Debug、Info、Warn、Error、Fatal)。这是一个非常好的问题,如果读者找到答案,也请告诉作者。
例 5-20
Logger类中各种日志方法的重载形式
- void fine(String msg)
- void fine(Supplier<String> msgSupplier)
- void finer(String msg)
- void finer(Supplier<String> msgSupplier)
- void finest(String msg)
- void finest(Supplier<String> msgSupplier)
- void info(String msg)
- void info(Supplier<String> msgSupplier)
- void warning(String msg)
- void warning(Supplier<String> msgSupplier)
- void severe(String msg)
- void severe(Supplier<String> msgSupplier)
每种方法的第一种重载形式(传入 String)是 Java 1.4 引入的,而第二种重载形式(传入 Supplier)是 Java 8 引入的,它在标准库中的实现如例 5-21 所示。
例 5-21
Logger类的实现细节
- public void info(Supplier<String> msgSupplier) {
- log(Level.INFO, msgSupplier);
- }
- public void log(Level level, Supplier<String> msgSupplier) {
- if (!isLoggable(level)) { ➊
- return;
- }
- LogRecord lr = new LogRecord(level, msgSupplier.get()); ➋
- doLog(lr);
- }
❶ 如果不显示消息则返回
❷ 调用 get 方法以便在 Supplier 中检索消息
上述实现并非构建一个永远不会显示的消息,而是检查消息是否是“可记录的”(loggable)。如果所提供的消息是一个简单的字符串,程序将评估它是否已被记录。在上述日志方法中,第二种重载形式(传入 Supplier)支持在消息前添加空括号和箭头(() ->)以将其转换为 Supplier,且仅当日志级别合适时才会调用它。例 5-22 显示了 info 方法的两种重载形式。
例 5-22 在
info方法中使用Supplier
- private Logger logger = Logger.getLogger(this.getClass().getName());
- private List<String> data = new ArrayList<>();
- // 用数据填充列表
- logger.info("The data is " + data.toString()); ➊
- logger.info(() -> "The data is " + data.toString()); ➋
❶ 无论是否显示 Info 消息,都会构建参数
❷ 仅当日志级别显示 Info 消息时,才会构建参数
可以看到,消息在列表的每个对象上调用 toString 方法。在第一条 logger.info 语句中,无论程序是否显示 Info 消息,都会创建结果字符串;在第二条 logger.info 语句中,在消息前添加 () -> 就能将日志参数转换为 Supplier,这意味着只有使用消息时才会调用 Supplier 的 get 方法。
采用相同类型的 Supplier 替换参数的技术称为延迟执行(deferred execution),可以在任何对象创建成本较高的上下文中使用。
另见
延迟执行是 Supplier 的主要用例之一,有关 Supplier 接口的讨论请参见范例 2.2。
