Java 9-Plattformprotokollierungs-API

Java 9 Platform Logging API

1. Einführung

In diesem Tutorial werden wir die neu eingeführte Protokollierungs-API in Java 9 untersuchen und einige Beispiele implementieren, um die häufigsten Fälle abzudecken.

Diese API wurde in Java inprovide a common mechanism to handle all the platform logs and to expose a service interface that can be customized by libraries and applications. eingeführt. Auf diese Weise können die JDK-Plattformprotokolle dasselbe Protokollierungsframework wie die Anwendung verwenden und die Projektabhängigkeiten können reduziert werden.

2. Erstellen einer benutzerdefinierten Implementierung

In diesem Abschnitt werden die Hauptklassen der Protokollierungs-API angezeigt, die wir implementieren müssen, um einen neuen Protokollierer zu erstellen. Dazu implementieren wir einen einfachen Logger, der alle Protokolle auf der Konsole druckt.

2.1. Logger erstellen

Die Hauptklasse, die wir erstellen müssen, istLogger. Diese Klasse muss mindestens dieSystem.Logger-Schnittstelle und diese vier Methoden implementieren:

  • getName(): Gibt den Namen des Loggers zurück. Es wird vom JDK verwendet, um Logger nach Namen zu erstellen

  • isLoggable(): Gibt an, für welche Ebenen der Logger aktiviert ist

  • log(): Dies ist die Methode, mit der das Protokoll auf dem zugrunde liegenden System gedruckt wird, das die Anwendung verwendet - in unserem Fall auf der Konsole. Es sind 2log()Methoden zu implementieren, von denen jede unterschiedliche Parameter empfängt

Mal sehen, wie unsere Implementierung aussehen wird:

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));
    }
}

UnsereConsoleLogger-Klasse überschreibt die vier genannten Methoden. Die MethodegetName() gibtString, zurück, während die MethodeisLoggable() in allen Fällentrue zurückgibt. Schließlich haben wir die 2log()-Methode, die an die Konsole ausgegeben wird.

2.2. LoggerFinder erstellen

Sobald wir unseren Logger erstellt haben,we need to implement a LoggerFinder that creates instances of our ConsoleLogger.

Dazu müssen wir die abstrakte KlasseSystem.LoggerFinder erweitern und die MethodegetLogger() implementieren:

public class CustomLoggerFinder extends System.LoggerFinder {

    @Override
    public System.Logger getLogger(String name, Module module) {
        return new ConsoleLogger();
    }
}

In diesem Fall geben wir immer unsereConsoleLoggerzurück.

Finally, we need to register our LoggerFinder as a Service so it can be discovered by the JDK. Wenn wir keine Implementierung bereitstellen, werden standardmäßigSimpleConsoleLogger verwendet.

Der vom JDK zum Laden der Implementierungen verwendete Mechanismus istServiceLoader. Weitere Informationen dazu finden Sie inthis tutorial.

Da wir Java 9 verwenden, packen wir unsere Klasse in ein Modul und registrieren unseren Service in der Dateimodule-info.java:

module com.example.logging {
    provides java.lang.System.LoggerFinder
      with com.example.logging.CustomLoggerFinder;
    exports com.example.logging;
}

Weitere Informationen zu Java-Modulen finden Sie unterthis other tutorial.

2.3. Testen Sie unser Beispiel

Um unser Beispiel zu testen, erstellen wir ein weiteres Modul, das als Anwendung fungiert. Dies enthält nur die KlasseMain, die unsere Service-Implementierung verwendet.

Diese Klasse erhält eine Instanz unsererConsoleLogger, indem sie dieSystem.getLogger()-Methode aufruft:

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");
    }
}

Intern übernimmt das JDK die Implementierung vonCustomLoggerFinderund erstellt eine Instanz unsererConsoleLogger.

Danach erstellen wir diemodule-info-Datei für dieses Modul:

module com.example.logging.app {
}

Zu diesem Zeitpunkt sieht unsere Projektstruktur folgendermaßen aus:

├── 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
└──

Schließlich werden wir unsere beiden Module kompilieren und sie in einemmods-Verzeichnis ablegen:

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

Lassen Sie uns abschließend dieMain-Klasse desapp-Moduls ausführen:

java --module-path mods \
  -m com.example.logging.app/com.example.logging.app.MainApp

Wenn wir uns die Konsolenausgabe ansehen, können wir sehen, dass unsere Protokolle mit unserenConsoleLogger gedruckt werden:

ConsoleLogger [ERROR]: error test
ConsoleLogger [INFO]: info test

3. Hinzufügen eines externen Protokollierungsframeworks

In unserem vorherigen Beispiel haben wir alle unsere Nachrichten an der Konsole protokolliert. Dies entspricht der Standardprotokollierung. 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, und das werden wir in diesem Abschnitt tun.

Wir werden ein neues Modul erstellen, das SLF4J als Protokollierungsfassade und Logback als Protokollierungsframework verwendet.

Da wir die Grundlagen bereits im vorherigen Abschnitt erläutert haben, können wir uns jetzt darauf konzentrieren, wie ein externes Protokollierungsframework hinzugefügt wird.

3.1. Benutzerdefinierte Implementierungen mit SLF4J

Zuerst implementieren wir weitereLogger, die für jede Instanz einen neuen SLF4J-Logger erstellen:

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;
    }

    //...
}

Beachten Sie, dass diesesLogger einorg.slf4j.Logger ist.

Für den Rest der Methoden giltwe’ll rely on the implementation on the SLF4J logger instance. Daher werden unsereLogger aktiviert, wenn der SLF4J-Logger aktiviert ist:

@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;
    }
}

Die Protokollmethoden rufen abhängig von der verwendeten Protokollstufe die entsprechende SLF4J-Protokollierungsmethode auf:

@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
    }
}

Zuletzt erstellen wir ein neuesLoggerFinder, das unsereSlf4jLogger verwendet:

public class Slf4jLoggerFinder extends System.LoggerFinder {
    @Override
    public System.Logger getLogger(String name, Module module) {
        return new Slf4jLogger(name);
    }
}

3.2. Modulkonfiguration

Sobald wir alle unsere Klassen implementiert haben, registrieren wir unseren Service in unserem Modul und fügen die Abhängigkeit des SLF4J-Moduls hinzu:

module com.example.logging.slf4j {
    requires org.slf4j;
    provides java.lang.System.LoggerFinder
      with com.example.logging.slf4j.Slf4jLoggerFinder;
    exports com.example.logging.slf4j;
}

Dieses Modul wird die folgende Struktur haben:

├── src
│   ├── modules
│   │   ├── com.example.logging.slf4j
│   │   │   ├── com
│   │   │   │   └── example
│   │   │   │       └── logging
│   │   │   │           └── slf4j
│   │   │   │               ├── Slf4jLoggerFinder.java
│   │   │   │               └── Slf4jLogger.java
│   │   │   └── module-info.java
└──

Jetzt können wir dieses Modul wie im vorherigen Abschnitt in das Verzeichnismods kompilieren.

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. Die neueste Version finden Sie inMaven Central.

3.3. Logback hinzufügen

Wir sind fast fertig, müssen aber noch die Logback-Abhängigkeiten und die Konfiguration hinzufügen. Platzieren Sie dazu die Gläserlogback-classic undlogback-core im Verzeichnismods.

As before, we have to make sure we’re using a modularized version of the library. Auch hier ist die neueste Version inMaven Central zu finden.

Zuletzt erstellen wir eine Logback-Konfigurationsdatei und legen sie in unseremmods-Verzeichnis ab:


    
        
            
                %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} -- %msg%n
            
        
    

    
        
    

3.4. Ausführen unserer Anwendung

Zu diesem Zeitpunkt können wir unsereapp mit unserem SLF4J-Modul ausführen.

In diesem Fallwe 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

Wenn wir die Ausgabe überprüfen, können wir schließlich sehen, dass unsere Protokolle mit unserer Rückmeldungskonfiguration gedruckt werden:

2018-08-25 14:02:40 [main] ERROR MainApp -- error test
2018-08-25 14:02:40 [main] INFO  MainApp -- info test

4. Fazit

In diesem Artikel wird gezeigt, wie Sie mithilfe der neuen Plattformprotokollierungs-API einen benutzerdefinierten Logger in Java 9 erstellen. Außerdem haben wir ein Beispiel mithilfe eines externen Protokollierungsframeworks implementiert. Dies ist einer der nützlichsten Anwendungsfälle dieser neuen API.

Wie immer ist der vollständige Quellcode der Beispieleover on GitHub verfügbar.