Java 9プラットフォームロギングAPI

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で入手できます。