简体   繁体   中英

Log4j2 does not change logging level at runtime

Yes, I've read all the related questions. I am using log4j2 (tried both version 2.4 and updating to latest, version 2.6.2).

I have a small utility program for customers. I'm keen to keep exposed configurations at minimum. But for problematic cases, I'd also want to add a -debug flag to enable debug logs at runtime.

Here is my code to enable debug logging

private static void enableDebugLogs(){
    LoggerContext ctx = (LoggerContext) LogManager.getContext();
    LoggerConfig log = ctx.getConfiguration().getRootLogger();

    System.out.println(log.getLevel()); // INFO

    log.setLevel(Level.DEBUG);

    System.out.println(log.getLevel()); // DEBUG

    ctx.updateLoggers();

    System.out.println(ctx.getRootLogger().getLevel()); // DEBUG, hey it works, right?
}

But it does not actually work with any of these cases:

enableDebugLogs();
logger.debug("Debug mode on"); // static, already made logger. Level did not change

LogManager.getLogger(Main.class).debug("Debug"); // Nope, not printing

Logger root = LogManager.getRootLogger();
root.info("Level: " + root.getLevel());  // Level: INFO, should be DEBUG

The utility program is finished usually in less than 30 seconds, so the change should be instant. Here is the log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level - %msg%n"/>
        </Console>
        <RollingFile name="File" fileName="program_name.log" filePattern="program_name-archived.log">
            <PatternLayout>
                <Pattern>%d{HH:mm:ss.SSS} %-5level - %msg%n</Pattern>
            </PatternLayout>
            <Policies>
                <SizeBasedTriggeringPolicy size="10 KB" />
            </Policies>
            <DefaultRolloverStrategy min="1" max="1"/>
        </RollingFile>
    </Appenders>
    <Loggers>
        <Root level="info">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="File"/>
        </Root>
    </Loggers>
</Configuration>

Is the problem with usings AppenderRefs? Can I somehow tell the Appenders to update logging level from Root logger?

Found the real issue. Had to use:

LoggerContext ctx = (LoggerContext) LogManager.getContext(false);

instead of

LoggerContext ctx = (LoggerContext) LogManager.getContext();

API stating the difference being "returns the LoggerContext" and "returns the current LoggerContext". And I clearly missed this bit of information for the version without boolean parameter:

"WARNING - The LoggerContext returned by this method may not be the LoggerContext used to create a Logger for the calling class."

You can change switch the logging configuration between two or multiple log4j2.xml files.
For example, create two log4j2.xml files with different configurations. log4j2.xml & log4j2-debug.xml and pass it to below code.

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

To reconfigure log4j2 after it's initialization, the documentation give you two ways :

  1. Using the config file, you can enable the automatic reconfiguration (it's the preferred way) so that modifying the file after initialization will be reflected on your runtime : http://logging.apache.org/log4j/2.x/manual/configuration.html#AutomaticReconfiguration
  2. Using ConfigurationBuilder you can reconfigure log4j programatically (I think it's what you are looking for), see the "Reconfigure Log4j Using ConfigurationBuilder with the Configurator" paragraph of this page : http://logging.apache.org/log4j/2.x/manual/customconfig.html

In addition to the above, to debug log4j initialization, set <Configuration status="trace" in the beginning of the configuration file. Log4j2's internal status logs will print to the console. This may help troubleshooting.

Here is a little known fact. If you set your ROOT_LOGGER level in your log4j2.xml or equivalent properties to say DEBUG, then setting the level dynamically to something lower did not work for me; in other words, setting dynamically had no effect. ONLY when I change the setting in log4j2.xml to TRACE and then using the below to change the log level did it work for me:

    Level level = Level.valueOf(this.consoleLogLevel.toUpperCase());
    //Dynamically set the log level for ALL loggers
    LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
    Configuration config = ctx.getConfiguration();
    LoggerConfig loggerConfig = config.getLoggerConfig(LogManager.ROOT_LOGGER_NAME);
    loggerConfig.setLevel(level);
    ctx.updateLoggers();

And here is my simple configuration file:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="TRACE">
            <AppenderRef level="TRACE" ref="Console" />
        </Root>
    </Loggers>
</Configuration>

And here is a TestNG test you can refactor to verify the above:

 @Test
    public void givenCut_whenLoggingWithERRORLevel_LoggingOutputIsAtERRORLevelAndHigherOnly() {
        try {
            lcaProperties.setLogLevel(Level.ERROR.name());
            GlobalLogger globalLogger = GlobalLogger.builder().lcaServiceProperties(lcaProperties).build();
            assertNotNull(globalLogger.getConsoleLogger());
            assertNotNull(globalLogger.getFunctionalDbLogger());

            assertEquals(globalLogger.getConsoleLogger().getLevel(), Level.ERROR);
            assertEquals(log.getLevel(), Level.ERROR);
            System.out.println("log.getLogLevel() = " + log.getLevel());
            System.out.println("globalLogger.getConsoleLogger().getLevel() = " + globalLogger.getConsoleLogger().getLevel());

            log.fatal("logLevel::"+log.getLevel()+"::log.fatal");
            log.error("logLevel::"+log.getLevel()+"::log.error");
            log.warn("logLevel::"+log.getLevel()+"::log.warn");
            log.info("logLevel::"+log.getLevel()+"::log.info");
            log.debug("logLevel::"+log.getLevel()+"::log.debug");
            log.trace("logLevel::"+log.getLevel()+"::log.trace");

            globalLogger.getConsoleLogger().fatal("logLevel::"+globalLogger.getConsoleLogger().getLevel()+"::globalLogger.getConsoleLogger().fatal");
            globalLogger.getConsoleLogger().error("logLevel::"+globalLogger.getConsoleLogger().getLevel()+"::globalLogger.getConsoleLogger().debug");
            globalLogger.getConsoleLogger().warn("logLevel::"+globalLogger.getConsoleLogger().getLevel()+"::globalLogger.getConsoleLogger().debug");
            globalLogger.getConsoleLogger().info("logLevel::"+globalLogger.getConsoleLogger().getLevel()+"::globalLogger.getConsoleLogger().debug");
            globalLogger.getConsoleLogger().debug("logLevel::"+globalLogger.getConsoleLogger().getLevel()+"::globalLogger.getConsoleLogger().debug");
            globalLogger.getConsoleLogger().trace("logLevel::"+globalLogger.getConsoleLogger().getLevel()+"globalLogger.getConsoleLogger()::log.trace");

        } catch (Exception e) {
            fail();
            log.error(e);
        }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM