Configuración programática con Log4j 2

1. Introducción

En este tutorial, veremos diferentes formas de configurar Apache Log4j 2 mediante programación.

2. Configuración inicial

Para comenzar a usar Log4j 2, simplemente necesitamos incluir las dependencias log4j-core y log4j-slf4j-impl en nuestro pom.xml :

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

3. ConfigurationBuilder

Una vez que tenemos Maven configurado, entonces necesitamos crear un ConfigurationBuilder , que es la clase que nos permite configurar appenders, filtros, diseños y registradores.

Log4j 2 proporciona varias formas de obtener un ConfigurationBuilder .

Empecemos por la forma más directa:

ConfigurationBuilder builder = ConfigurationBuilderFactory.newConfigurationBuilder();

Y para comenzar a configurar componentes, ConfigurationBuilder está equipado con un nuevo método correspondiente , como newAppender o newLayout , para cada componente.

Algunos componentes tienen subtipos diferentes, como FileAppender o ConsoleAppender, y en la API se hace referencia a ellos como complementos .

3.1. Configuración de anexos

Digamos al constructor dónde enviar cada línea de registro configurando 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);

Si bien la mayoría de los métodos nuevos no admiten esto, newAppender (nombre, complemento) nos permite darle un nombre al appender, que resultará importante más adelante. A estos appenders los hemos llamado stdout y log, aunque podríamos haberlos nombrado de cualquier manera.

También le hemos dicho al constructor qué complemento de appender (o, más simplemente, qué tipo de appender) usar. Consola y Archivo se refieren a los anexos de Log4j 2 para escribir en la salida estándar y el sistema de archivos, respectivamente.

Aunque Log4j 2 admite varios appenders, configurarlos usando Java puede ser un poco complicado ya que AppenderComponentBuilder es una clase genérica para todos los tipos de appenders.

Esto hace que tenga métodos como addAttribute y addComponent en lugar de setFileName y 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); 

Y, finalmente, ¡no olvide llamar a builder.add para agregarlo a la configuración principal!

3.2. Configurar filtros

Podemos agregar filtros a cada uno de nuestros anexos, que deciden en cada línea de registro si se debe agregar o no.

Usemos el MarkerFilter plugin en nuestra appender consola:

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

Tenga en cuenta que este nuevo método no nos permite nombrar el filtro, pero nos pide que indiquemos qué hacer si el filtro pasa o falla.

En este caso, lo hemos mantenido simple, indicando que si el MarkerFilter pasa, entonces ACEPTE el logline. De lo contrario, NEGARLO .

Tenga en cuenta en este caso que no agregamos esto al constructor sino a los agregadores que queremos usar este filtro.

3.3. Configurar diseños

A continuación, definamos el diseño de cada línea de registro. En este caso, usaremos el complemento PatternLayout :

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

Nuevamente, los hemos agregado directamente a los appenders apropiados en lugar de al constructor directamente.

3.4. Configuración del registrador raíz

Ahora que sabemos a dónde se enviarán los registros, queremos configurar qué registros irán a cada destino.

El registrador raíz es el registrador más alto, algo así como Object en Java. Este registrador es el que se utilizará de forma predeterminada a menos que se anule.

Entonces, usemos un registrador raíz para establecer el nivel de registro predeterminado en ERROR y el appender predeterminado para nuestro appender stdout desde arriba:

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

Para apuntar nuestro registrador a un appender específico, no le damos una instancia del constructor. En cambio, nos referimos a él por el nombre que le dimos anteriormente.

3.5. Configuración de registradores adicionales

Los registradores secundarios se pueden utilizar para apuntar a paquetes específicos o nombres de registradores.

Agreguemos un registrador para el paquete com en nuestra aplicación, estableciendo el nivel de registro en DEBUG y haciendo que estos vayan a nuestro appender de log :

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

Tenga en cuenta que podemos establecer la aditividad con nuestros registradores, lo que indica si este registrador debe heredar propiedades como el nivel de registro y los tipos de agregadores de sus antepasados.

3.6. Configurar otros componentes

No todos los componentes tienen un nuevo método dedicado en ConfigurationBuilder .

Entonces, en ese caso, llamamos 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.

Podemos tomar ese archivo de configuración y enviarlo a nuestra implementación personalizada de XmlConfiguration, donde podemos colocar cualquier configuración primordial que necesitemos:

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

5. Conclusión

En este artículo, analizamos cómo usar la nueva API ConfigurationBuilder disponible en Log4j 2.

También echamos un vistazo a la personalización de ConfigurationFactory en combinación con ConfigurationBuilder para casos de uso más avanzados.

No olvide consultar mis ejemplos completos en GitHub.