Java 9 Platform Logging API

1. Einleitung

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 eingeführt, um einen gemeinsamen Mechanismus für die Verarbeitung aller Plattformprotokolle bereitzustellen und eine Dienstschnittstelle bereitzustellen, die von Bibliotheken und Anwendungen angepasst werden kann. 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 zeigen wir die Hauptklassen der Protokollierungs-API, 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. Erstellen der Logger

Die Hauptklasse, die wir erstellen müssen, ist der Logger . Diese Klasse muss mindestens die System.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 das zugrunde liegende System gedruckt wird, das die Anwendung verwendet - in unserem Fall auf die Konsole. Es sind 2 log () -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)); } }

Unsere ConsoleLogger- Klasse überschreibt die vier genannten Methoden. Die Methode getName () gibt einen String zurück, während die Methode isLoggable () in allen Fällen true zurückgibt . Schließlich haben wir die 2 log () -Methode, die an die Konsole ausgegeben wird.

2.2. Erstellen der LoggerFinder

Sobald wir unseren Logger erstellt haben, müssen wir einen LoggerFinder implementieren , der Instanzen unseres ConsoleLogger erstellt .

Dazu müssen wir die abstrakte Klasse System.LoggerFinder erweitern und die Methode getLogger () 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 unseren ConsoleLogger zurück .

Schließlich müssen wir unseren LoggerFinder als Service registrieren, damit er vom JDK erkannt werden kann . Wenn wir keine Implementierung bereitstellen, wird standardmäßig der SimpleConsoleLogger verwendet.

Der vom JDK zum Laden der Implementierungen verwendete Mechanismus ist der ServiceLoader . Weitere Informationen dazu finden Sie in diesem Tutorial.

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

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

Weitere Informationen zu Java-Modulen finden Sie in diesem anderen Lernprogramm.

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 Hauptklasse , die unsere Service-Implementierung verwendet.

Diese Klasse erhält eine Instanz unseres ConsoleLogger durch Aufrufen der System.getLogger () -Methode:

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 nimmt das JDK unsere CustomLoggerFinder- Implementierung auf und erstellt eine Instanz unseres ConsoleLogger.

Danach erstellen wir die Modul-Info- Datei für dieses Modul:

module com.baeldung.logging.app { }

Zu diesem Zeitpunkt sieht unsere Projektstruktur folgendermaßen aus:

├── src │   ├── modules │   │   ├── com.baeldung.logging │   │   │   ├── com │   │   │   │   └── baeldung │   │   │   │   └── logging │   │   │   │   ├── ConsoleLogger.java │   │   │   │   └── CustomLoggerFinder.java │   │   │   └── module-info.java │   │   ├── com.baeldung.logging.app │   │   │   ├── com │   │   │   │   └── baeldung │   │   │   │   └── logging │   │   │   │   └── app │   │   │   │   └── MainApp.java │   │   │   └── module-info.java └──

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

javac --module-path mods -d mods/com.baeldung.logging \ src/modules/com.baeldung.logging/module-info.java \ src/modules/com.baeldung.logging/com/baeldung/logging/*.java javac --module-path mods -d mods/com.baeldung.logging.app \ src/modules/com.baeldung.logging.app/module-info.java \ src/modules/com.baeldung.logging.app/com/baeldung/logging/app/*.java

Lassen Sie uns abschließend die Hauptklasse des App- Moduls ausführen :

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

Wenn wir uns die Konsolenausgabe ansehen, können wir sehen, dass unsere Protokolle mit unserem ConsoleLogger 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 dem Standardlogger. Eine der nützlichsten Anwendungen der Protokollierungs-API in Java 9 besteht darin, dass Anwendungen die JDK-Protokolle an dasselbe Protokollierungsframework weiterleiten, das die Anwendung verwendet. Dies 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 einen weiteren Logger , der für jede Instanz einen neuen SLF4J-Logger erstellt:

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 dieser Logger ein org.slf4j.Logger ist .

Für den Rest der Methoden verlassen wir uns auf die Implementierung auf der SLF4J-Logger-Instanz . Daher wird unser Logger 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 einen neuen LoggerFinder , der unseren Slf4jLogger verwendet :

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

3.2. Module Configuration

Once we have all our classes implemented, let's register our service in our module and add the dependency of the SLF4J module:

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

This module will have the following structure:

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

Now we can compile this module into the mods directory as we did in the previous section.

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. The latest version can be found in Maven Central.

3.3. Adding Logback

We're almost done, but we still need to add the Logback dependencies and configuration. To do so, place the logback-classic and logback-core jars in the mods directory.

As before, we have to make sure we're using a modularized version of the library. Again, the latest version can be found in Maven Central.

Finally, let's create a Logback configuration file and place it in our mods directory:

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

3.4. Running Our Application

At this point, we can run our app using our SLF4J module.

In this case, we also need to specify our Logback configuration file:

java --module-path mods \ -Dlogback.configurationFile=mods/logback.xml \ -m com.baeldung.logging.app/com.baeldung.logging.app.MainApp

Finally, if we check the output we can see that our logs are printed using our Logback configuration:

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

4. Conclusion

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

Wie immer ist der vollständige Quellcode der Beispiele auf GitHub verfügbar.