API de journalisation de la plate-forme Java 9

1. Introduction

Dans ce didacticiel, nous explorerons la nouvelle API de journalisation dans Java 9 et implémenterons quelques exemples pour couvrir les cas les plus courants.

Cette API a été introduite dans Java pour fournir un mécanisme commun permettant de gérer tous les journaux de la plateforme et d'exposer une interface de service qui peut être personnalisée par des bibliothèques et des applications. De cette façon, les journaux de la plate-forme JDK peuvent utiliser le même cadre de journalisation que l'application, et les dépendances du projet peuvent être réduites.

2. Création d'une implémentation personnalisée

Dans cette section, nous allons montrer les principales classes de l'API Logging que nous devons implémenter pour créer un nouvel enregistreur. Nous le ferons en implémentant un simple journal qui imprime tous les journaux sur la console.

2.1. Création de l' enregistreur

La classe principale que nous devons créer est le Logger . Cette classe doit implémenter au moins l' interface System.Logger et ces quatre méthodes:

  • getName () : renvoie le nom de l'enregistreur. Il sera utilisé par le JDK pour créer des enregistreurs par nom
  • isLoggable () : indique les niveaux pour lesquels l'enregistreur est activé
  • log () : c'est la méthode qui imprime le journal sur n'importe quel système sous-jacent utilisé par l'application - la console dans notre cas. Il y a 2 méthodes log () à implémenter, chacune recevant des paramètres différents

Voyons à quoi ressemblera notre implémentation:

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

Notre classe ConsoleLogger remplace les quatre méthodes mentionnées. La méthode getName () renvoie une chaîne, tandis que la méthode isLoggable () renvoie true dans tous les cas. Enfin, nous avons la méthode 2 log () qui sort sur la console.

2.2. Création du LoggerFinder

Une fois notre enregistreur créé, nous devons implémenter un LoggerFinder qui crée des instances de notre ConsoleLogger .

Pour ce faire, nous devons étendre la classe abstraite System.LoggerFinder et implémenter la méthode getLogger () :

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

Dans ce cas, nous retournons toujours notre ConsoleLogger .

Enfin, nous devons enregistrer notre LoggerFinder en tant que service afin qu'il puisse être découvert par le JDK . Si nous ne fournissons pas d'implémentation, SimpleConsoleLogger sera utilisé par défaut.

Le mécanisme utilisé par le JDK pour charger les implémentations est le ServiceLoader . Vous pouvez trouver plus d'informations à ce sujet dans ce tutoriel.

Puisque nous utilisons Java 9, nous allons empaqueter notre classe dans un module et enregistrer notre service dans le fichier module-info.java :

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

Pour plus d'informations sur les modules Java, consultez cet autre didacticiel.

2.3. Tester notre exemple

Pour tester notre exemple, créons un autre module qui agira comme une application. Cela ne contiendra que la classe Main qui utilise notre implémentation de service.

Cette classe obtiendra une instance de notre ConsoleLogger en appelant la méthode System.getLogger () :

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

En interne, le JDK récupérera notre implémentation CustomLoggerFinder et créera une instance de notre ConsoleLogger.

Après cela, créons le fichier module-info pour ce module:

module com.baeldung.logging.app { }

À ce stade, la structure de notre projet ressemblera à ceci:

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

Enfin, nous allons compiler nos deux modules, et nous les placerons dans un répertoire mods :

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

Finally, let's run the Main class of the app module:

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

If we take a look at the console output we can see that our logs are printed using our ConsoleLogger:

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

3. Adding an External Logging Framework

In our previous example, we were logging all our messages to the console, which is the same as what the default logger does. 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, and that's what we're going to do in this section.

We'll create a new module that uses SLF4J as logging facade and Logback as logging framework.

Since we've already explained the basics in the previous section, now we can focus on how to add an external logging framework.

3.1. Custom Implementations Using SLF4J

First, we'll implement another Logger that will create a new SLF4J logger for each instance:

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

Notice that this Logger is an org.slf4j.Logger.

For the rest of the methods, we'll rely on the implementation on the SLF4J logger instance. Therefore, our Logger will be enabled if the SLF4J logger is enabled:

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

And the log methods will call the appropriate SLF4J logger method depending on the log level used:

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

Finally, let's create a new LoggerFinder that uses our Slf4jLogger:

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

Nous avons montré dans cet article comment créer un enregistreur personnalisé dans Java 9 à l'aide de la nouvelle API Platform Logging. De plus, nous avons implémenté un exemple en utilisant un framework de journalisation externe, qui est l'un des cas d'utilisation les plus utiles de cette nouvelle API.

Comme toujours, le code source complet des exemples est disponible à l'adresse over sur GitHub.