Cambiar programáticamente el nivel de registro en Log4j2

109

Estoy interesado en cambiar programáticamente el nivel de registro en Log4j2. Intenté mirar su documentación de configuración pero no parecía tener nada. También intenté buscar en el paquete:, org.apache.logging.log4j.core.configpero tampoco nada me pareció útil.

CorayThan
fuente
2
Si no obtiene una respuesta aquí, pruebe con la lista de correo, generalmente los autores principales la revisan una vez cada dos días. Luego regrese y responda su propia pregunta :-)
tgkprog

Respuestas:

139

EDITADO según log4j2 versión 2.4 FAQ

Puede establecer el nivel de un registrador con la clase Configurador de Log4j Core. PERO tenga en cuenta que la clase Configurator no es parte de la API pública.

// org.apache.logging.log4j.core.config.Configurator;
Configurator.setLevel("com.example.Foo", Level.DEBUG);

// You can also set the root logger:
Configurator.setRootLevel(Level.DEBUG);

Fuente

EDITADO para reflejar los cambios en la API introducidos en Log4j2 versión 2.0.2

Si desea cambiar el nivel del registrador raíz, haga algo como esto:

LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
Configuration config = ctx.getConfiguration();
LoggerConfig loggerConfig = config.getLoggerConfig(LogManager.ROOT_LOGGER_NAME); 
loggerConfig.setLevel(level);
ctx.updateLoggers();  // This causes all Loggers to refetch information from their LoggerConfig.

Aquí está el javadoc para LoggerConfig.

Slaadvak
fuente
3
Correcto y si desea cambiar solo por un registrador en particular (de una clase / paquete) obtenga el contexto de ese registrador, setLevel y updateLoggers.
tgkprog
34
Una forma tan complicada de establecer el nivel de registro. Estoy seguro de que hay una razón para hacerlo con cinco líneas de código en lugar de la línea original en versiones anteriores de log4j, pero simplemente no lo veo. En cualquier caso, ¡gracias por esto, @slaadvak!
Sturm
1
.updateLoggers () no parece ser necesario. Parece que los cambios realizados con .setLevel () se aplican de inmediato.
zbyszek
1
La llamada a updateLoggers siempre es necesaria, incluso si agrega un nuevo LoggerConfig. UpdateLoggers hace que todos los registradores se asocien con LoggerConfigs y cambien su nivel de registro al de su LoggerConfig asociado. Si agrega un nuevo LoggerConfig, cualquier Logger que coincida con el nuevo patrón LoggerConfig será redirigido a él. Se requiere la forma "complicada" porque los registradores y su configuración se han separado en Log4j2.
asistentes
1
Aquí hay una respuesta que proporciona una solución actualizada de 1 o 2 líneas para las versiones más nuevas de Log4J: stackoverflow.com/a/44678752/1339923
Lambart
37

La respuesta aceptada por @slaadvak no me funcionó para Log4j2 2.8.2 . Lo siguiente hizo.

Para cambiar el registro Level universalmente use:

Configurator.setAllLevels(LogManager.getRootLogger().getName(), level);

Para cambiar el registro Levelsolo para la clase actual, use:

Configurator.setLevel(LogManager.getLogger(CallingClass.class).getName(), level);
4myle
fuente
1
Obtuve mi voto porque extrajo los nombres de los registradores de los mismos registradores en lugar de codificar el nombre como una cadena.
DWoldrich
18

Si desea cambiar un único nivel de registrador específico (no el registrador raíz o los registradores configurados en el archivo de configuración) puede hacer esto:

public static void setLevel(Logger logger, Level level) {
    final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
    final Configuration config = ctx.getConfiguration();

    LoggerConfig loggerConfig = config.getLoggerConfig(logger.getName());
    LoggerConfig specificConfig = loggerConfig;

    // We need a specific configuration for this logger,
    // otherwise we would change the level of all other loggers
    // having the original configuration as parent as well

    if (!loggerConfig.getName().equals(logger.getName())) {
        specificConfig = new LoggerConfig(logger.getName(), level, true);
        specificConfig.setParent(loggerConfig);
        config.addLogger(logger.getName(), specificConfig);
    }
    specificConfig.setLevel(level);
    ctx.updateLoggers();
}
Jörg Friedrich
fuente
3
Esto no afectó en absoluto a mi registrador. Solía setLevel(logger, Level.ERROR);y declaraciones logger.debug siendo impresa. Mi archivo log4j2.xml está en pastebin.com/fcbV2mTW
Noumenon
Actualicé el código. Avísame si hay algún problema con él.
Jörg Friedrich
4
En log4j 2.7 LoggerContext no tiene un método getConfiguration (), consulte logging.apache.org/log4j/2.x/log4j-api/apidocs/index.html?org/…
maxxyme
log4j-core-2.7.jar tiene y puede usarse como LoggerContext final ctx = (LoggerContext) LogManager.getContext (false); Configuración de configuración final = ctx.getConfiguration ();
jprism
3
Después de ver esto, estoy pensando ... "¿Quizás no quieren que cambiemos el nivel en tiempo de ejecución?"
Koray Tugay
13

Encontré una buena respuesta aquí: https://garygregory.wordpress.com/2016/01/11/changing-log-levels-in-log4j2/

Puede usar org.apache.logging.log4j.core.config.Configurator para establecer el nivel de un registrador específico.

Logger logger = LogManager.getLogger(Test.class);
Configurator.setLevel(logger.getName(), Level.DEBUG);
alfred.schalk
fuente
2
Esta respuesta muestra la misma solución, así como cómo configurarla para el registrador raíz, que a veces es útil: stackoverflow.com/a/44678752/1339923
Lambart
4

El enfoque programático es bastante intrusivo. Quizás debería verificar el soporte JMX brindado por Log4J2:

  1. Habilite el puerto JMX en el inicio de su aplicación:

    -Dcom.sun.management.jmxremote.port = [port_num]

  2. Utilice cualquiera de los clientes JMX disponibles (la JVM proporciona uno en JAVA_HOME / bin / jconsole.exe) mientras ejecuta su aplicación.

  3. En JConsole, busque el bean "org.apache.logging.log4j2.Loggers"

  4. Finalmente cambie el nivel de su registrador

Lo que más me gusta de esto es que no tienes que modificar tu código o configuración para administrarlo. Todo es externo y transparente.

Más información: http://logging.apache.org/log4j/2.x/manual/jmx.html

Víctor
fuente
¡Muy útil, gracias!
Darren Parker
3

La mayoría de las respuestas asumen por defecto que el registro tiene que ser aditivo. Pero digamos que algún paquete está generando muchos registros y desea desactivar el registro solo para ese registrador en particular. Aquí está el código que usé para que funcione

    public class LogConfigManager {

    public void setLogLevel(String loggerName, String level) {
        Level newLevel = Level.valueOf(level);
        LoggerContext logContext = (LoggerContext) LogManager.getContext(false);
        Configuration configuration = logContext.getConfiguration();
        LoggerConfig loggerConfig = configuration.getLoggerConfig(loggerName);
        // getLoggerConfig("a.b.c") could return logger for "a.b" if there is no logger for "a.b.c"
        if (loggerConfig.getName().equalsIgnoreCase(loggerName)) {
            loggerConfig.setLevel(newLevel);
            log.info("Changed logger level for {} to {} ", loggerName, newLevel);
        } else {
            // create a new config.
            loggerConfig = new LoggerConfig(loggerName, newLevel, false);
            log.info("Adding config for: {} with level: {}", loggerConfig, newLevel);
            configuration.addLogger(loggerName, loggerConfig);


            LoggerConfig parentConfig = loggerConfig.getParent();
            do {
                for (Map.Entry<String, Appender> entry : parentConfig.getAppenders().entrySet()) {
                    loggerConfig.addAppender(entry.getValue(), null, null);
                }
                parentConfig = parentConfig.getParent();
            } while (null != parentConfig && parentConfig.isAdditive());
        }
        logContext.updateLoggers();
    }
}

Un caso de prueba para lo mismo

public class LogConfigManagerTest {
    @Test
    public void testLogChange() throws IOException {
        LogConfigManager logConfigManager = new LogConfigManager();
        File file = new File("logs/server.log");
        Files.write(file.toPath(), new byte[0], StandardOpenOption.TRUNCATE_EXISTING);
        Logger logger = LoggerFactory.getLogger("a.b.c");
        logger.debug("Marvel-1");
        logConfigManager.setLogLevel("a.b.c", "debug");
        logger.debug("DC-1");
        // Parent logger level should remain same
        LoggerFactory.getLogger("a.b").debug("Marvel-2");
        logConfigManager.setLogLevel("a.b.c", "info");
        logger.debug("Marvel-3");
        // Flush everything
        LogManager.shutdown();

        String content = Files.readAllLines(file.toPath()).stream().reduce((s1, s2) -> s1 + "\t" + s2).orElse(null);
        Assert.assertEquals(content, "DC-1");
    }
}

Suponiendo que el siguiente log4j2.xml está en classpath

<?xml version="1.0" encoding="UTF-8"?>
<Configuration xmlns="http://logging.apache.org/log4j/2.0/config">

    <Appenders>
        <File name="FILE" fileName="logs/server.log" append="true">
            <PatternLayout pattern="%m%n"/>
        </File>
        <Console name="STDOUT" target="SYSTEM_OUT">
            <PatternLayout pattern="%m%n"/>
        </Console>
    </Appenders>

    <Loggers>
        <AsyncLogger name="a.b" level="info">
            <AppenderRef ref="STDOUT"/>
            <AppenderRef ref="FILE"/>
        </AsyncLogger>

        <AsyncRoot level="info">
            <AppenderRef ref="STDOUT"/>
        </AsyncRoot>
    </Loggers>

</Configuration>
duende
fuente
2

Una forma poco habitual que encontré para hacerlo es crear dos archivos separados con diferentes niveles de registro.
Por ejemplo. log4j2.xml y log4j-debug.xml Ahora cambie la configuración de estos archivos.
Código de muestra:

ConfigurationFactory configFactory = XmlConfigurationFactory.getInstance();
            ConfigurationFactory.setConfigurationFactory(configFactory);
            LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
            ClassLoader classloader = Thread.currentThread().getContextClassLoader();
            InputStream inputStream = classloader.getResourceAsStream(logFileName);
            ConfigurationSource configurationSource = new ConfigurationSource(inputStream);

            ctx.start(configFactory.getConfiguration(ctx, configurationSource));
dpp.2325
fuente