Java 9プラットフォームロギングAPI
1. 前書き
このチュートリアルでは、Java9で新しく導入されたLoggingAPIについて説明し、最も一般的なケースをカバーするいくつかの例を実装します。
このAPIはJavaでprovide a common mechanism to handle all the platform logs and to expose a service interface that can be customized by libraries and applications.に導入されました。このようにして、JDKプラットフォームログはアプリケーションと同じログフレームワークを使用でき、プロジェクトの依存関係を減らすことができます。
2. カスタム実装の作成
このセクションでは、新しいロガーを作成するために実装する必要があるLoggingAPIのメインクラスを示します。 これを行うには、すべてのログをコンソールに出力する単純なロガーを実装します。
2.1. Loggerの作成
作成する必要のある主なクラスはLoggerです。 このクラスは、System.Loggerインターフェイスと少なくとも次の4つのメソッドを実装する必要があります。
-
getName():ロガーの名前を返します。 JDKが名前でロガーを作成するために使用します
-
isLoggable():ロガーが有効になっているレベルを示します
-
log():これは、アプリケーションが使用している基盤となるシステム(この場合はコンソール)にログを出力するメソッドです。 実装する2つのlog()メソッドがあり、それぞれが異なるパラメーターを受け取ります
実装がどのようになるか見てみましょう。
public class ConsoleLogger implements System.Logger {
@Override
public String getName() {
return "ConsoleLogger";
}
@Override
public boolean isLoggable(Level level) {
return true;
}
@Override
public void log(Level level, ResourceBundle bundle, String msg, Throwable thrown) {
System.out.printf("ConsoleLogger [%s]: %s - %s%n", level, msg, thrown);
}
@Override
public void log(Level level, ResourceBundle bundle, String format, Object... params) {
System.out.printf("ConsoleLogger [%s]: %s%n", level,
MessageFormat.format(format, params));
}
}
ConsoleLoggerクラスは、前述の4つのメソッドをオーバーライドします。 getName()メソッドはString,を返しますが、isLoggable()メソッドはすべての場合にtrueを返します。 最後に、コンソールに出力する2log()メソッドがあります。
2.2. LoggerFinderの作成
ロガーを作成したら、we need to implement a LoggerFinder that creates instances of our ConsoleLogger.
そのためには、抽象クラスSystem.LoggerFinderを拡張し、getLogger()メソッドを実装する必要があります。
public class CustomLoggerFinder extends System.LoggerFinder {
@Override
public System.Logger getLogger(String name, Module module) {
return new ConsoleLogger();
}
}
この場合、常にConsoleLoggerを返します。
Finally, we need to register our LoggerFinder as a Service so it can be discovered by the JDK。 実装を提供しない場合、デフォルトでSimpleConsoleLoggerが使用されます。
JDKが実装をロードするために使用するメカニズムはServiceLoaderです。 詳細については、this tutorialを参照してください。
Java 9を使用しているので、クラスをモジュールにパッケージ化し、サービスをmodule-info.javaファイルに登録します。
module com.example.logging {
provides java.lang.System.LoggerFinder
with com.example.logging.CustomLoggerFinder;
exports com.example.logging;
}
Javaモジュールの詳細については、this other tutorialを確認してください。
2.3. 例のテスト
この例をテストするために、アプリケーションとして機能する別のモジュールを作成しましょう。 これには、サービス実装を使用するMainクラスのみが含まれます。
このクラスは、System.getLogger()メソッドを呼び出すことにより、ConsoleLoggerのインスタンスを取得します。
public class MainApp {
private static System.Logger LOGGER = System.getLogger("MainApp");
public static void main(String[] args) {
LOGGER.log(Level.ERROR, "error test");
LOGGER.log(Level.INFO, "info test");
}
}
内部的には、JDKはCustomLoggerFinder実装を取得し、ConsoleLogger.のインスタンスを作成します
その後、このモジュールのmodule-infoファイルを作成しましょう。
module com.example.logging.app {
}
この時点で、プロジェクトの構造は次のようになります。
├── src
│ ├── modules
│ │ ├── com.example.logging
│ │ │ ├── com
│ │ │ │ └── example
│ │ │ │ └── logging
│ │ │ │ ├── ConsoleLogger.java
│ │ │ │ └── CustomLoggerFinder.java
│ │ │ └── module-info.java
│ │ ├── com.example.logging.app
│ │ │ ├── com
│ │ │ │ └── example
│ │ │ │ └── logging
│ │ │ │ └── app
│ │ │ │ └── MainApp.java
│ │ │ └── module-info.java
└──
最後に、2つのモジュールをコンパイルし、それらをmodsディレクトリに配置します。
javac --module-path mods -d mods/com.example.logging \
src/modules/com.example.logging/module-info.java \
src/modules/com.example.logging/com/example/logging/*.java
javac --module-path mods -d mods/com.example.logging.app \
src/modules/com.example.logging.app/module-info.java \
src/modules/com.example.logging.app/com/example/logging/app/*.java
最後に、appモジュールのMainクラスを実行してみましょう。
java --module-path mods \
-m com.example.logging.app/com.example.logging.app.MainApp
コンソール出力を見ると、ログがConsoleLoggerを使用して出力されていることがわかります。
ConsoleLogger [ERROR]: error test
ConsoleLogger [INFO]: info test
3. 外部ロギングフレームワークの追加
前の例では、すべてのメッセージをコンソールに記録していましたが、これはデフォルトのロガーが行うことと同じです。 One of the most useful uses of the Logging API in Java 9 is to let applications route the JDK logs to the same logging framework the application is using、これがこのセクションで行うことです。
SLF4Jをロギングファサードとして使用し、Logbackをロギングフレームワークとして使用する新しいモジュールを作成します。
前のセクションで基本をすでに説明したので、次に、外部ロギングフレームワークを追加する方法に焦点を当てることができます。
3.1. SLF4Jを使用したカスタム実装
まず、インスタンスごとに新しいSLF4Jロガーを作成する別のLoggerを実装します。
public class Slf4jLogger implements System.Logger {
private final String name;
private final Logger logger;
public Slf4jLogger(String name) {
this.name = name;
logger = LoggerFactory.getLogger(name);
}
@Override
public String getName() {
return name;
}
//...
}
このLogger はorg.slf4j.Loggerであることに注意してください。
残りのメソッドについては、we’ll rely on the implementation on the SLF4J logger instance。 したがって、SLF4Jロガーが有効になっている場合、Loggerが有効になります。
@Override
public boolean isLoggable(Level level) {
switch (level) {
case OFF:
return false;
case TRACE:
return logger.isTraceEnabled();
case DEBUG:
return logger.isDebugEnabled();
case INFO:
return logger.isInfoEnabled();
case WARNING:
return logger.isWarnEnabled();
case ERROR:
return logger.isErrorEnabled();
case ALL:
default:
return true;
}
}
そして、ログメソッドは、使用されるログレベルに応じて適切なSLF4J loggerメソッドを呼び出します。
@Override
public void log(Level level, ResourceBundle bundle, String msg, Throwable thrown) {
if (!isLoggable(level)) {
return;
}
switch (level) {
case TRACE:
logger.trace(msg, thrown);
break;
case DEBUG:
logger.debug(msg, thrown);
break;
case INFO:
logger.info(msg, thrown);
break;
case WARNING:
logger.warn(msg, thrown);
break;
case ERROR:
logger.error(msg, thrown);
break;
case ALL:
default:
logger.info(msg, thrown);
}
}
@Override
public void log(Level level, ResourceBundle bundle, String format, Object... params) {
if (!isLoggable(level)) {
return;
}
String message = MessageFormat.format(format, params);
switch (level) {
case TRACE:
logger.trace(message);
break;
// ...
// same as the previous switch
}
}
最後に、Slf4jLoggerを使用する新しいLoggerFinderを作成しましょう。
public class Slf4jLoggerFinder extends System.LoggerFinder {
@Override
public System.Logger getLogger(String name, Module module) {
return new Slf4jLogger(name);
}
}
3.2. モジュール構成
すべてのクラスを実装したら、サービスをモジュールに登録し、SLF4Jモジュールの依存関係を追加しましょう。
module com.example.logging.slf4j {
requires org.slf4j;
provides java.lang.System.LoggerFinder
with com.example.logging.slf4j.Slf4jLoggerFinder;
exports com.example.logging.slf4j;
}
このモジュールの構造は次のとおりです。
├── src
│ ├── modules
│ │ ├── com.example.logging.slf4j
│ │ │ ├── com
│ │ │ │ └── example
│ │ │ │ └── logging
│ │ │ │ └── slf4j
│ │ │ │ ├── Slf4jLoggerFinder.java
│ │ │ │ └── Slf4jLogger.java
│ │ │ └── module-info.java
└──
これで、前のセクションで行ったように、このモジュールをmodsディレクトリにコンパイルできます。
Notice that we have to place the slf4j-api jar in the mods directory to compile this module. Also, keep in mind to use a modularized version of the library.最新バージョンはMaven Centralにあります。
3.3. ログバックの追加
ほぼ完了しましたが、ログバックの依存関係と構成を追加する必要があります。 これを行うには、logback-classicおよびlogback-core jarをmodsディレクトリに配置します。
As before, we have to make sure we’re using a modularized version of the library。 繰り返しますが、最新バージョンはMaven Centralにあります。
最後に、ログバック構成ファイルを作成して、modsディレクトリに配置します。
%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} -- %msg%n
3.4. アプリケーションの実行
この時点で、SLF4Jモジュールを使用してappを実行できます。
この場合、we also need to specify our Logback configuration file:
java --module-path mods \
-Dlogback.configurationFile=mods/logback.xml \
-m com.example.logging.app/com.example.logging.app.MainApp
最後に、出力を確認すると、Logback構成を使用してログが印刷されていることがわかります。
2018-08-25 14:02:40 [main] ERROR MainApp -- error test
2018-08-25 14:02:40 [main] INFO MainApp -- info test
4. 結論
この記事では、新しいPlatform LoggingAPIを使用してJava9でカスタムロガーを作成する方法を示しました。 また、この新しいAPIの最も有用なユースケースの1つである、外部ロギングフレームワークを使用した例を実装しました。
いつものように、例の完全なソースコードはover on GitHubで入手できます。