Configuration par programmation avec Log4j 2

1. Introduction

Dans ce didacticiel, nous examinerons différentes manières de configurer par programmation Apache Log4j 2.

2. Configuration initiale

Pour commencer à utiliser Log4j 2, nous devons simplement inclure les dépendances log4j-core et log4j-slf4j-impl dans notre pom.xml :

 org.apache.logging.log4j log4j-core 2.11.0   org.apache.logging.log4j log4j-slf4j-impl 2.11.0 

3. ConfigurationBuilder

Une fois que nous avons configuré Maven, nous devons créer un ConfigurationBuilder , qui est la classe qui nous permet de configurer des appenders, des filtres, des mises en page et des enregistreurs.

Log4j 2 propose plusieurs méthodes pour obtenir un ConfigurationBuilder .

Commençons par le moyen le plus direct:

ConfigurationBuilder builder = ConfigurationBuilderFactory.newConfigurationBuilder();

Et pour commencer à configurer les composants, ConfigurationBuilder est équipé d'une nouvelle méthode correspondante , comme newAppender ou newLayout , pour chaque composant.

Certains composants ont différents sous-types, tels que FileAppender ou ConsoleAppender, et ceux-ci sont appelés plugins dans l'API .

3.1. Configurer les appenders

Disons au constructeur où envoyer chaque ligne de journal en configurant un appender :

AppenderComponentBuilder console = builder.newAppender("stdout", "Console"); builder.add(console); AppenderComponentBuilder file = builder.newAppender("log", "File"); file.addAttribute("fileName", "target/logging.log"); builder.add(file);

Bien que la plupart des nouvelles méthodes ne le prennent pas en charge, newAppender (nom, plugin) nous permet de donner un nom à l'appender, ce qui s'avérera important plus tard. Ces appenders, nous les avons appelés stdout et log, même si nous aurions pu les nommer n'importe quoi.

Nous avons également indiqué au constructeur quel plugin appender (ou, plus simplement, quel type d'appender) utiliser. Console et File font référence aux appenders de Log4j 2 pour l'écriture dans la sortie standard et le système de fichiers, respectivement.

Bien que Log4j 2 prenne en charge plusieurs appenders, leur configuration à l'aide de Java peut être un peu délicate car AppenderComponentBuilder est une classe générique pour tous les types d'appender.

Cela lui donne des méthodes comme addAttribute et addComponent au lieu de setFileName et addTriggeringPolicy :

AppenderComponentBuilder rollingFile = builder.newAppender("rolling", "RollingFile"); rollingFile.addAttribute("fileName", "rolling.log"); rollingFile.addAttribute("filePattern", "rolling-%d{MM-dd-yy}.log.gz"); builder.add(rollingFile); 

Et, enfin, n'oubliez pas d'appeler builder.add pour l'ajouter à la configuration principale!

3.2. Configuration des filtres

Nous pouvons ajouter des filtres à chacun de nos appenders, qui décident sur chaque ligne de journal si elle doit être ajoutée ou non.

Utilisons le plugin MarkerFilter sur notre appender console:

FilterComponentBuilder flow = builder.newFilter( "MarkerFilter", Filter.Result.ACCEPT, Filter.Result.DENY); flow.addAttribute("marker", "FLOW"); console.add(flow);

Notez que cette nouvelle méthode ne nous permet pas de nommer le filtre, mais elle nous demande d'indiquer ce qu'il faut faire si le filtre réussit ou échoue.

Dans ce cas, nous avons gardé les choses simples, déclarant que si le MarkerFilter réussit, alors ACCEPTER la ligne de connexion. Sinon, REFUSEZ- le.

Notez dans ce cas que nous n'ajoutons pas ceci au générateur mais plutôt aux ajouteurs que nous voulons utiliser ce filtre.

3.3. Configuration des dispositions

Ensuite, définissons la disposition de chaque ligne de journal. Dans ce cas, nous utiliserons le plugin PatternLayout :

LayoutComponentBuilder standard = builder.newLayout("PatternLayout"); standard.addAttribute("pattern", "%d [%t] %-5level: %msg%n%throwable"); console.add(standard); file.add(standard); rolling.add(standard);

Encore une fois, nous les avons ajoutés directement aux appenders appropriés au lieu de directement au constructeur .

3.4. Configuration de l'enregistreur racine

Maintenant que nous savons où les journaux seront expédiés, nous voulons configurer les journaux qui iront à chaque destination.

L'enregistreur racine est l'enregistreur le plus élevé, un peu comme Object en Java. Cet enregistreur est ce qui sera utilisé par défaut à moins d'être remplacé.

Alors, utilisons un enregistreur racine pour définir le niveau de journalisation par défaut sur ERROR et l'appender par défaut pour notre appender stdout par le haut:

RootLoggerComponentBuilder rootLogger = builder.newRootLogger(Level.ERROR); rootLogger.add(builder.newAppenderRef("stdout")); builder.add(rootLogger);

Pour pointer notre enregistreur vers un appender spécifique, nous ne lui donnons pas d'instance du constructeur. Au lieu de cela, nous y faisons référence par le nom que nous lui avons donné plus tôt.

3.5. Configuration d'enregistreurs supplémentaires

Les enregistreurs enfants peuvent être utilisés pour cibler des packages ou des noms d'enregistreurs spécifiques.

Ajoutons un enregistreur pour le package com dans notre application, en définissant le niveau de journalisation sur DEBUG et en les faisant accéder à notre appender de journal :

LoggerComponentBuilder logger = builder.newLogger("com", Level.DEBUG); logger.add(builder.newAppenderRef("log")); logger.addAttribute("additivity", false); builder.add(logger);

Notez que nous pouvons définir l' additivité avec nos enregistreurs, qui indique si cet enregistreur doit hériter des propriétés telles que le niveau de journalisation et les types d'appendeurs de ses ancêtres.

3.6. Configuration d'autres composants

Tous les composants n'ont pas une nouvelle méthode dédiée sur ConfigurationBuilder .

Donc, dans ce cas, nous appelons newComponent.

For example, because there isn't a TriggeringPolicyComponentBuilder, we need to use newComponent for something like specifying our triggering policy for rolling file appenders:

ComponentBuilder triggeringPolicies = builder.newComponent("Policies") .addComponent(builder.newComponent("CronTriggeringPolicy") .addAttribute("schedule", "0 0 0 * * ?")) .addComponent(builder.newComponent("SizeBasedTriggeringPolicy") .addAttribute("size", "100M")); rolling.addComponent(triggeringPolicies);

3.7. The XML Equivalent

ConfigurationBuilder comes equipped with a handy method to print out the equivalent XML:

builder.writeXmlConfiguration(System.out);

Running the above line prints out:

This comes in handy when we want to double-check our configuration or if we want to persist our configuration, say, to the file system.

3.8. Putting It All Together

Now that we are fully configured, let's tell Log4j 2 to use our configuration:

Configurator.initialize(builder.build());

After this is invoked, future calls to Log4j 2 will use our configuration.

Note that this means that we need to invoke Configurator.initialize before we make any calls to LogManager.getLogger.

4. ConfigurationFactory

Now that we've seen one way to get and apply a ConfigurationBuilder, let's take a look at one more:

public class CustomConfigFactory extends ConfigurationFactory { public Configuration createConfiguration( LoggerContext context, ConfigurationSource src) { ConfigurationBuilder builder = super .newConfigurationBuilder(); // ... configure appenders, filters, etc. return builder.build(); } public String[] getSupportedTypes() { return new String[] { "*" }; } }

In this case, instead of using ConfigurationBuilderFactory, we subclassed ConfigurationFactory, an abstract class targetted for creating instances of Configuration.

Then, instead of calling Configurator.initialize like we did the first time, we simply need to let Log4j 2 know about our new configuration factory.

There are three ways to do this:

  • Static initialization
  • A runtime property, or
  • The @Plugin annotation

4.1. Use Static Initialization

Log4j 2 supports calling setConfigurationFactory during static initialization:

static { ConfigurationFactory custom = new CustomConfigFactory(); ConfigurationFactory.setConfigurationFactory(custom); }

This approach has the same limitation as for the last approach we saw, which is that we'll need to invoke it before any calls to LogManager.getLogger.

4.2. Use a Runtime Property

If we have access to the Java startup command, then Log4j 2 also supports specifying the ConfigurationFactory to use via a -D parameter:

-Dlog4j2.configurationFactory=com.baeldung.log4j2.CustomConfigFactory

The main benefit of this approach is that we don't have to worry about initialization order as we do with the first two approaches.

4.3. Use the @Plugin Annotation

And finally, in circumstances where we don't want to fiddle with the Java startup command by adding a -D, we can simply annotate our CustomConfigurationFactory with the Log4j 2 @Plugin annotation:

@Plugin( name = "CustomConfigurationFactory", category = ConfigurationFactory.CATEGORY) @Order(50) public class CustomConfigFactory extends ConfigurationFactory { // ... rest of implementation }

Log4j 2 will scan the classpath for classes having the @Plugin annotation, and, finding this class in the ConfigurationFactory category, will use it.

4.4. Combining With Static Configuration

Another benefit to using a ConfigurationFactory extension is that we can easily combine our custom configuration with other configuration sources like XML:

public Configuration createConfiguration( LoggerContext context, ConfigurationSource src) { return new WithXmlConfiguration(context, src); } 

The source parameter represents the static XML or JSON configuration file that Log4j 2 finds if any.

Nous pouvons prendre ce fichier de configuration et l'envoyer à notre implémentation personnalisée de XmlConfiguration où nous pouvons placer toute configuration de remplacement dont nous avons besoin:

public class WithXmlConfiguration extends XmlConfiguration { @Override protected void doConfigure() { super.doConfigure(); // parse xml document // ... add our custom configuration } }

5. Conclusion

Dans cet article, nous avons examiné comment utiliser la nouvelle API ConfigurationBuilder disponible dans Log4j 2.

Nous avons également examiné la personnalisation de ConfigurationFactory en combinaison avec ConfigurationBuilder pour des cas d'utilisation plus avancés.

N'oubliez pas de consulter mes exemples complets sur GitHub.