2

Change the log level for my appenders programmatically or be influenced from another source.

Dear community.

Log4J2 Version is: 2.12.0

My application is using log4j2.xml and the log level is set to INFO. My application is also reading another configuration file, where I want to put the log level for my users e.g DEBUG.

When the application is initalized (from log4j2.xml), I want change the level for all to DEBUG from second source as an example. So I want override the log4j2.xml INFO to DEBUG from my file (programmatically).

I read some tips and tricks here in the forum about Configurator.setRootLevel, Configurator.setAllLevels etc, but I can't get it working. Like here: Programmatically change log level in Log4j2

Example for testing is:

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.config.Configurator;

public class A {
    private static Logger logger = LogManager.getLogger(A.class);
//    private static Logger logger = null;

    public static void main(String[] args) {
    Configurator.setAllLevels(LogManager.getRootLogger().getName(), Level.DEBUG);
    logger.info("A INFO message1");
    logger.debug("A DEBUG message2");
    logger.info("A INFO message3");
    }
}

log4j2.xml:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
  <Appenders>
    <Console name="STDOUT" target="SYSTEM_OUT">
      <PatternLayout pattern="[%d] [%-5p] [%-7t] - %m%n" charset="CP850" />
    </Console>
    <RollingFile name="RollingFile" fileName="abc.log" filePattern="abc-%d{yyyyMMdd}.log.gz" ignoreExceptions="false">
      <PatternLayout>
        <Pattern>[%d] [%-5p] [%-7t] - %m [%l]%n</Pattern>
      </PatternLayout>
      <Policies>
        <SizeBasedTriggeringPolicy size="10MB" />
      </Policies>
      <DefaultRolloverStrategy max="10" />
    </RollingFile>
  </Appenders>
  <Loggers>
    <Root level="INFO">
      <AppenderRef ref="STDOUT" level="INFO" />
      <AppenderRef ref="RollingFile" level="INFO"/>
    </Root>
  </Loggers>
</Configuration>

So I want to get the DEBUG information in my appenders. Any hints?

Thanks in advance.

ula.uvula
  • 158
  • 13

1 Answers1

2

I would strongly suggest that you do NOT use a programmatic solution for this because it will make your code depend on the implementation details of log4j2. This will make long term maintenance of your code problematic if the implementation changes. So, if you don't want to go with the JMX approach then you could set up your log4j2 configuration to use a DynamicThresholdFilter.

Here is a simple example of the filter approach:

First, a log4j2.xml file to configure the log4j2 system:

<?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" />
            <DynamicThresholdFilter key="myLogLvl" defaultThreshold="ERROR"
                                      onMatch="ACCEPT" onMismatch="DENY">
                <KeyValuePair key="TRACE" value="TRACE"/>
                <KeyValuePair key="DEBUG" value="DEBUG"/>
                <KeyValuePair key="INFO" value="INFO"/>
                <KeyValuePair key="WARN" value="WARN"/>
                <KeyValuePair key="FATAL" value="FATAL"/>
              </DynamicThresholdFilter>
        </Console>
    </Appenders>

    <Loggers>
        <Root level="ALL">
            <AppenderRef ref="Console" />
        </Root>
    </Loggers>
</Configuration>

Notice how I have set up the filter so that the DynamicThresholdFilter has a key of "myLogLvl" and then several KeyValuePair that define the log level threshold based on the value in ThreadContext for the key "myLogLvl". I assumed you wanted to use the names of the levels as the value you would put into ThreadContext.

Also notice that I have set the Root logger's level to "ALL". This is so that all messages will be accepted by the logger and then filtered by the filter. In other words I'm putting the filter in control of which messages are accepted rather than the logger.

Here is a simple Java class to generate some logs:

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.ThreadContext;

public class SomeClass {

    private static final Logger log = LogManager.getLogger();

    public static void main(String[] args){

        ThreadContext.put("myLogLvl", "WARN");

        if(log.isDebugEnabled())
            log.debug("This is some debug! (This should not appear in console)");
        log.info("Here's some info! (This should not appear in console)");
        log.error("Some error happened! (We will see this in the console)");

        //Maybe now I want INFO log level
        ThreadContext.put("myLogLvl", "INFO");

        log.info("This should now appear in the console");
        log.debug("This still should --not-- appear");
        log.fatal("This will also appear");
    }
}

Notice the first I do is set the ThreadContext variable "myLogLvl" to "WARN" so that only messages that are "WARN" level or more specific are accepted. If you don't do this there will be no value for "myLogLvl" which means the default level defined in the filter will not apply either, so all messages will be accepted.

Here is sample output generated by the above:

10:39:44.668 [main] ERROR example.SomeClass - Some error happened! (We will see this in the console)
10:39:44.670 [main] INFO  example.SomeClass - This should now appear in the console
10:39:44.670 [main] FATAL example.SomeClass - This will also appear

Hope this helps!

D.B.
  • 4,523
  • 2
  • 19
  • 39
  • Hello D.B. Your solution works great for me. So I can read the loglevel from my second source and set programmatically. You saved my day. Thanks. – ula.uvula Nov 12 '19 at 08:32