Java 9 - 平台日志 API

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

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

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

输出如下

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 中的

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

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

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

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

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

既然 System.Logger 是一个门面,那么它通过自定的 System.Logger 和 System.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。 

因为 JDK/JVM 用 System.Logger 来输出日志,它默认使用 JUL(当该模块存在时) 实现,所以我们只要用 jul-to-slf4j bridge 就能按需把 JDK/JVM 的日志输出到我们想要的地方去。

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

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

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

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

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

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

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

ABadCurrencyFile

然后执行代码

这会去触发 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 也是在控制平台的日志输出。

链接:

  1. Java 9 - Platform Logging API and Service

本文链接 https://yanbin.blog/java-9-platform-logging-api/, 来自 隔叶黄莺 Yanbin Blog

[版权声明] Creative Commons License 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。

Subscribe
Notify of
guest

8 Comments
Inline Feedbacks
View all comments
wayne
wayne
5 years ago

我现在有一个问题,一直想搞清楚
我在method里面创建一个对象,这个对象是在堆里面存放到还是在栈里面存放的呢?method执行完,这个对象会被回收吗?
如果我想看对象创建后在堆区还是在栈区,有没有什么工具可以跟踪到,或者说怎么去证明这个对象在堆中呢?
看到您这个文章,利用这个新的logger接口,有没有可能去实现这样一个证明呢?

wayne
wayne
5 years ago
Reply to  Yanbin

最近听到一个说法,对象创建并不一定都是存放在堆中,有可能会创建在栈里面。我今天发现要证明对象存放在堆中还是可以借助jstat命令来实现,但是要证明不在堆中,没想到方法,有什么命令可以来监测栈吗?

wayne
wayne
5 years ago
Reply to  Yanbin

可以设置栈上分配,现在1.8默认就是开启的,有些对象通过逃逸分析,别认定可以逃逸的情况下,jvm默认会分配到栈上的

wayne
wayne
5 years ago
Reply to  Yanbin

这块,我倒是还没有看到呢,分配在栈上的对象进行传递。
这里说的对象传递是传递给另外方法吗?相当于把对象传出去?还有拷贝是拷贝到其他到栈还是到堆里面呢?
其实控制逃逸分析倒是可以控制,主要是写代码到时候需要刻意去按照逃逸到方式编写,应该是可以实现的。