API de journalisation Java 9 Platform

API de journalisation Java 9 Platform

1. introduction

Dans ce didacticiel, nous allons explorer la nouvelle API Logging dans Java 9 et mettre en œuvre quelques exemples pour couvrir les cas les plus courants.

Cette API a été introduite dans Java pourprovide a common mechanism to handle all the platform logs and to expose a service interface that can be customized by libraries and 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éer 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 allons le faire en implémentant un simple enregistreur qui imprime tous les journaux sur la console.

2.1. Création desLogger

La classe principale que nous devons créer est lesLogger. Cette classe doit implémenter au moins l'interfaceSystem.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 le système sous-jacent utilisé par l'application - la console dans notre cas. Il y a 2log()méthodes à implémenter, chacune recevant des paramètres différents

Voyons à quoi ressemblera notre mise en œuvre:

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 classeConsoleLogger remplace les quatre méthodes mentionnées. La méthodegetName() renvoie unString, tandis que la méthodeisLoggable() renvoietrue dans tous les cas. Enfin, nous avons la méthode 2log() qui sort sur la console.

2.2. Création desLoggerFinder

Une fois notre enregistreur créé,we need to implement a LoggerFinder that creates instances of our ConsoleLogger.

Pour ce faire, nous devons étendre la classe abstraiteSystem.LoggerFinder et implémenter la méthodegetLogger():

public class CustomLoggerFinder extends System.LoggerFinder {

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

Dans ce cas, nous renvoyons toujours nosConsoleLogger.

Finally, we need to register our LoggerFinder as a Service so it can be discovered by the JDK. Si nous ne fournissons pas d'implémentation, lesSimpleConsoleLogger seront utilisés par défaut.

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

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

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

Pour plus d'informations sur les modules Java, consultezthis other tutorial.

2.3. Tester notre exemple

Pour tester notre exemple, créons un autre module qui fera office d’application. Cela ne contiendra que la classeMain qui utilise notre implémentation de service.

Cette classe obtiendra une instance de nosConsoleLogger en appelant la méthodeSystem.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émentationCustomLoggerFinder et créera une instance de nosConsoleLogger.

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

module com.example.logging.app {
}

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

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

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

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

Enfin, exécutons la classeMain du moduleapp:

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

Si nous jetons un œil à la sortie de la console, nous pouvons voir que nos journaux sont imprimés en utilisant nosConsoleLogger:

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

3. Ajout d'un cadre de journalisation externe

Dans notre exemple précédent, nous enregistrions tous nos messages sur la console, ce qui est identique à ce que fait le consignateur par défaut. 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, et c'est ce que nous allons faire dans cette section.

Nous allons créer un nouveau module qui utilise SLF4J comme façade de journalisation et Logback comme cadre de journalisation.

Puisque nous avons déjà expliqué les principes de base dans la section précédente, nous pouvons maintenant nous concentrer sur l'ajout d'un cadre de journalisation externe.

3.1. Implémentations personnalisées utilisant SLF4J

Tout d'abord, nous allons implémenter un autreLogger qui créera un nouvel enregistreur SLF4J pour chaque 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;
    }

    //...
}

Notez que ceLogger  est unorg.slf4j.Logger.

Pour le reste des méthodes,we’ll rely on the implementation on the SLF4J logger instance. Par conséquent, nosLogger seront activés si l'enregistreur SLF4J est activé:

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

Et les méthodes de journalisation appellent la méthode de journalisation SLF4J appropriée en fonction du niveau de journalisation utilisé:

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

Enfin, créons un nouveauLoggerFinder qui utilise nosSlf4jLogger:

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

3.2. Configuration du module

Une fois toutes nos classes implémentées, enregistrons notre service dans notre module et ajoutons la dépendance du module 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;
}

Ce module aura la structure suivante:

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

Nous pouvons maintenant compiler ce module dans le répertoiremods comme nous l'avons fait dans la section précédente.

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. La dernière version se trouve dansMaven Central.

3.3. Ajout de Logback

Nous avons presque terminé, mais nous devons encore ajouter les dépendances et la configuration de Logback. Pour ce faire, placez les fichiers jarlogback-classic etlogback-core dans le répertoiremods.

As before, we have to make sure we’re using a modularized version of the library. Encore une fois, la dernière version peut être trouvée dansMaven Central.

Enfin, créons un fichier de configuration Logback et placez-le dans notre répertoiremods:


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

    
        
    

3.4. Lancer notre application

À ce stade, nous pouvons exécuter nosapp en utilisant notre module SLF4J.

Dans ce cas,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

Enfin, si nous vérifions le résultat, nous pouvons voir que nos journaux sont imprimés à l'aide de notre configuration Logback:

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 mis en œuvre un exemple utilisant un cadre 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 disponibleover on GitHub.