API di registrazione della piattaforma Java 9

1. Introduzione

In questo tutorial, esploreremo l'API di registrazione appena introdotta in Java 9 e implementeremo alcuni esempi per coprire i casi più comuni.

Questa API è stata introdotta in Java per fornire un meccanismo comune per gestire tutti i log della piattaforma e per esporre un'interfaccia di servizio che può essere personalizzata da librerie e applicazioni. In questo modo, i log della piattaforma JDK possono utilizzare lo stesso framework di registrazione dell'applicazione e le dipendenze del progetto possono essere ridotte.

2. Creazione di un'implementazione personalizzata

In questa sezione, mostreremo le classi principali dell'API di registrazione che dobbiamo implementare per creare un nuovo logger. Lo faremo implementando un semplice logger che stampa tutti i log sulla console.

2.1. Creazione del logger

La classe principale che dobbiamo creare è il Logger . Questa classe deve implementare l' interfaccia System.Logger e questi quattro metodi almeno:

  • getName () : restituisce il nome del logger. Verrà utilizzato da JDK per creare logger per nome
  • isLoggable () : indica per quali livelli è abilitato il logger
  • log () : è il metodo che stampa il registro su qualunque sistema sottostante l'applicazione stia utilizzando, la console nel nostro caso. Esistono 2 metodi log () da implementare, ognuno dei quali riceve parametri diversi

Vediamo come apparirà la nostra implementazione:

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

La nostra classe ConsoleLogger sovrascrive i quattro metodi menzionati. Il getName () restituisce una stringa, mentre l'isLoggable () restituisce vero in tutti i casi. Infine, abbiamo il metodo 2 log () che genera l'output sulla console.

2.2. Creazione di LoggerFinder

Una volta creato il nostro logger, dobbiamo implementare un LoggerFinder che crea istanze del nostro ConsoleLogger .

Per fare ciò, dobbiamo estendere la classe astratta System.LoggerFinder e implementare il metodo getLogger () :

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

In questo caso, restituiamo sempre il nostro ConsoleLogger .

Infine, dobbiamo registrare il nostro LoggerFinder come servizio in modo che possa essere scoperto da JDK . Se non forniamo un'implementazione, SimpleConsoleLogger verrà utilizzato per impostazione predefinita.

Il meccanismo utilizzato da JDK per caricare le implementazioni è ServiceLoader . Puoi trovare maggiori informazioni a riguardo in questo tutorial.

Dato che stiamo usando Java 9, impacchetteremo la nostra classe in un modulo e registreremo il nostro servizio nel file module-info.java :

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

Per ulteriori informazioni sui moduli Java, controlla questo altro tutorial.

2.3. Testare il nostro esempio

Per testare il nostro esempio, creiamo un altro modulo che fungerà da applicazione. Questo conterrà solo la classe Main che utilizza la nostra implementazione del servizio.

Questa classe otterrà un'istanza del nostro ConsoleLogger chiamando il metodo 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"); } }

Internamente, il JDK raccoglierà la nostra implementazione CustomLoggerFinder e creerà un'istanza del nostro ConsoleLogger.

Dopodiché, creiamo il file module-info per questo modulo:

module com.baeldung.logging.app { }

A questo punto, la struttura del nostro progetto sarà simile a questa:

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

Infine, compileremo i nostri due moduli e li posizioneremo in una directory 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

Infine, eseguiamo la classe Main del modulo dell'app :

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

Se diamo uno sguardo all'output della console possiamo vedere che i nostri log vengono stampati utilizzando il nostro ConsoleLogger :

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

3. Aggiunta di un framework di registrazione esterno

Nel nostro esempio precedente, stavamo registrando tutti i nostri messaggi sulla console, che è lo stesso di ciò che fa il logger predefinito. Uno degli usi più utili dell'API di registrazione in Java 9 è consentire alle applicazioni di instradare i registri JDK allo stesso framework di registrazione utilizzato dall'applicazione , e questo è ciò che faremo in questa sezione.

Creeremo un nuovo modulo che utilizza SLF4J come facciata di registrazione e Logback come framework di registrazione.

Poiché abbiamo già spiegato le basi nella sezione precedente, ora possiamo concentrarci su come aggiungere un framework di registrazione esterno.

3.1. Implementazioni personalizzate che utilizzano SLF4J

Innanzitutto, implementeremo un altro logger che creerà un nuovo logger SLF4J per ogni istanza:

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

Notare che questo Logger è un org.slf4j.Logger .

Per il resto dei metodi, faremo affidamento sull'implementazione sull'istanza del logger SLF4J . Pertanto, il nostro logger sarà abilitato se il logger SLF4J è abilitato:

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

E i metodi di registro chiameranno il metodo di registrazione SLF4J appropriato a seconda del livello di registro utilizzato:

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

Infine, creiamo un nuovo LoggerFinder che utilizza il nostro 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

In questo articolo abbiamo mostrato come creare un logger personalizzato in Java 9 utilizzando la nuova API di registrazione della piattaforma. Inoltre, abbiamo implementato un esempio utilizzando un framework di registrazione esterno, che è uno dei casi d'uso più utili di questa nuova API.

Come sempre, il codice sorgente completo degli esempi è disponibile su GitHub.