转载

Java 9 - 平台日志 API

关于 Java 9 的新特性从某本书的最后一个说起:平台日志 API。个人没感觉这个有什么实质的用途,所谓的平台日志是指 JDK 自身代码,或者是 JVM 组件中的日志输出,而在自己应用程序代码中却不会去用这个平台日志 API。这个所谓的 Platform Logging API 名称的意义也就是在这里,平台用的,在诊断时用来观察 JDK 类或 JVM 中的日志输出,比如应该可以截获到  JVM 本地代码实现中的日志输出。对我们在项目中如何处理日志并不会有什么影响,该怎么还是怎么,不过了解多一点东西应该不会浪费脑容量的。

新加的平台日志体现在 java.lang.System 中新加的几个方法和类

Java 9 - 平台日志 API

我们可以尝试着在代码使用一下它

System.Logger logger = System.getLogger(TestLogging.class.getName());
logger.log(System.Logger.Level.INFO, "Hello Java 9 Platform Logging API");

输出如下

May 26, 2018 10:56:51 AM cc.unmi.TestLogging main  INFO: Hello Java 9 Platform Logging API

新 API 有 

java.lang.System.Logger 接口

java.lang.System.LoggerFinder 用来查找上面 Logger 的实现类

java.lang.System.getLogger(...) 方法,它们会通过 LoggerFinder 找到相应的 Logger 实现

默认的,System.getLogger(...) 会使用 JDK 的 java.util.logging (如果该模块存在时,即 JUL,它默认只输入 INFO 及更高级别的日志) 作为它的日志实现,所以前面的日志输出其实就是 JUL 的输出。见包 sun.util.logging.internal 中的

public final class LoggingProviderImpl extends DefaultLoggerFinder {
    ........
    static final class JULWrapper extends LoggerConfiguration
            implements System.Logger, PlatformLogger.Bridge,
                                      PlatformLogger.ConfigurableBridge {
        ........
    }
}

System.LoggerFinder 和  System.Logger 默认就是这两个实现类。

如果 JUL 模块不存在, System.Logger 将把 INFO 及更高级别的日志输出到 System.err 标准错误输出。

由此我们看到 System.Logger 也就是作为 JUL 的一个门面,老实说它提供的日志输出方法还不如 JUL 的 java.util.logging.Logger 友好。 System.Logger 的每个日志方法都要带上日志级别

Java 9 - 平台日志 API

JUL 的 Logger 除了提供带日志级别为参数的通用 log(Level, ...) 方法外,还为每一个日志级别提供了像下面那些便利方法(以 warning 为例)

warning(String msg)  warning(Supplier<String> msgSuppiler)

既然 System.Logger 是一个门面,那么它通过自定的 System.LoggerSystem.LoggerFinder 也能让它的日志桥接到其他日志框架上去,如 Apache Commong Logging(JCL - Jakarta Commons Logging), SLF4J, 或具体的日志实现 Log4j 或 Logback 去。

Java 9 新加这么一个日志框架门面显得有些累赘了, JUL 我们就不会去用它,有了更好的 SLF4J 更不会去用 System.Logger 。不过它也说了 JDK 自己的代码会用 System.Logger , 那么我们有可能要做的就是如何把 JDK 中的 System.Logger 日志输出导向到我们熟悉的日志框架中来。

日志框架越发复杂了,应该说来,目前更广为人知的用法还是 SLF4J + Log4J 或 SLF4J + Logback, 我更推崇后者。至于 JCL + Log4J 的用法越发稀少,而 Logback 比之 JCL 是个新鲜事物,它基本就是为 SLF4J 设计的。当前业界为了各个组件中使用的不同日志框架而统一日志输出目标,产生了诸如以下适配器

 jcl-over-slf4j, slf4j-jcl, log4j-over-slf4j, slf4j-log4j12, jul-to-slf4j

倒是没有找到 slf4j-over-jcl 这样的东西,因此 SLF4J 作为日志框架的门面还是当前趋势。简单项目中直接用 Log4J 或 Logback 也是可以的,Logback  自身就依赖了 SLF4J。

那么 Java 9 新推个 System.Logger 目的何在呢?可能也就是程序诊断上,基本不用去理会它,如果 JDK 自己用它来输出日志,那么就让它用默认的 JUL(当该模块存在时) 实现,我们只要用 jul-to-slf4j bridge 就能按需把 JDK 的日志输出到我们想要的地方去。

附:前面说了 JUL 默认只显示 INFO 或更高级别的日志,也就是 CONFIG, FINE, FINER, FINEST 的日志不会显示,那么要降低日志输出级别该怎么做呢?下面代码将开启所有日志输出

Logger rootLogger = LogManager.getLogManager().getLogger("");
rootLogger.setLevel(Level.ALL);
for (Handler hander : rootLogger.getHandlers()) {
    hander.setLevel(Level.ALL);
}

JUL 的日志级别还与 System.Logger 的日志级别还有差异(它们原本就不是一家人)

JUL 的日志级别:OFF, SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST, ALL

System.Logger 的日志级别分别是:OFF, ERROR, WARNING, INFO, DEBUG, TRACE, ALL   ---- 这个更符合主流意识形态

System.Logger 默认采用 JUL 来输出日志时,它们的日志级别映射关系可查看 sun.util.logging.PlatformLogger

  ALL(System.Logger.Level.ALL),
  FINEST(System.Logger.Level.TRACE),
  FINER(System.Logger.Level.TRACE),
  FINE(System.Logger.Level.DEBUG),
  CONFIG(System.Logger.Level.DEBUG),
  INFO(System.Logger.Level.INFO),
  WARNING(System.Logger.Level.WARNING),
  SEVERE(System.Logger.Level.ERROR),
  OFF(System.Logger.Level.OFF);

Java 9 的 Platform Logging API 基本上就说这么多了,只是让大家了解一下 Java 9 中有这么一个东西,它能做什么 事。并没有像某书中那样纠缠于如何自定义 System.LoggerSystem.LoggerFinder ,以及应用 SPI 来把平台日志输出到 Log4J 去,因为那么做的现实意义不大,我们项目中只需要专注于  SLF4J + Logback 的应用。

一个 Java 平台类使用 System.Logger 的例子

java.util.Currency 类中就使用到了 System.Logger

    private static void info(String message, Throwable t) {
        PlatformLogger logger = PlatformLogger.getLogger("java.util.Currency");
        if (logger.isLoggable(PlatformLogger.Level.INFO)) {
            if (t != null) {
                logger.info(message, t);
            } else {
                logger.info(message);
            }
        }
    }

当该类在初始化时如果不能解析文件 JAVA_HOME/lib/currency.properties 文件时就报错

static {
    ......
    // look for the properties file for overrides
    String propsFile = System.getProperty("java.util.currency.data");
    if (propsFile == null) {
        propsFile = System.getProperty("java.home") + File.separator + "lib" +
            File.separator + "currency.properties";
    }
    try {
        File propFile = new File(propsFile);
        if (propFile.exists()) {
            ......//parse currency.properties file
        }
    } catch (IOException e) {
        info("currency.properties is ignored because of an IOException", e);
    }
    ......
}

所以假如我们创建文件 JAVA_HOME/lib/currency.properties , 并在其中放入不合法的内容,如

ABadCurrencyFile

然后执行代码

Currency.getInstance("USD");

这会去触发 Currency 的 static 块,文件无法解析,控制台可以看到输出

May 26, 2018 1:19:15 PM java.util.Currency info  INFO: currency.properties entry for ABADCURENCYFILE is ignored because of the invalid country code.

这就是平台日志的用法,还有新增的虚拟机参数 -Xlog 也是在控制平台的日志输出。

原文  https://unmi.cc/java-9-platform-logging-api/
正文到此结束
Loading...