Log4j2: Ability to log data with different levels of logs in a multi-user environment

I am using log4j2 in oracle adf 12c app.

One of our client's requirements is to have different log levels for different registered users, as well as dynamically change log levels for the user. Also, the administrator must have control to stop all logging.

ie Let's say User A needs a trace log level and User B needs an error log level. If both users log on at the same time, the application must enter Trace Level for "User A" and "Error Level" for "User B". And if "User B" wants to enter FATAL, he will have to dynamically change the configuration.

Below is the log4j2 config file.

    <?xml version="1.0" encoding="UTF-8"?>
<Configuration status="trace">
    <MapFilter onMatch="ACCEPT" operator="or">
        <KeyValuePair key="$${ctx:LOGLEVELYN}" value="Y"/>
    </MapFilter>
    <Appenders>
        <File name="file" fileName="./adfAppCustomLogs/TestLog4j2.log">
            <PatternLayout>
                <Pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} %-5level %class %L %M - %msg%xEx%n</Pattern>
            </PatternLayout>
        </File>
        <Console name="STDOUT" target="SYSTEM_OUT">
            <PatternLayout pattern="%m%n"/>
        </Console>
        <Routing name="AppRouting">
            <Routes pattern="$${ctx:LOGGEDSESSIONID}">
                <!-- This route is chosen if ThreadContext has no value for key ROUTINGKEY. -->
                <Route key="$${ctx:LOGGEDSESSIONID}">
                    <RollingFile name="Rolling-ALL" fileName="./adfAppCustomLogs/DefaultAll.log"
                                 filePattern="./adfAppCustomLogs/archive/${date:yyyy-MM}/DefaultAll-%d{MM-dd-yyyy}-%i.txt.gz">
                        <PatternLayout>
                            <Pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} %-5level %t %msg%xEx%n</Pattern>
                        </PatternLayout>
                        <Policies>
                            <TimeBasedTriggeringPolicy interval="6" modulate="true"/>
                            <SizeBasedTriggeringPolicy size="10 MB"/>
                        </Policies>
                    </RollingFile>
                </Route>
                <!-- This route is chosen if ThreadContext has value 'user' for key ROUTINGKEY. -->
                <Route>
                    <RollingFile name="Rolling-OTHER-${ctx:LOGGEDSESSIONID}"
                                 fileName="./adfAppCustomLogs/${ctx:LOGINID}-${ctx:LOGGEDSESSIONID}.log"
                                 filePattern="./adfAppCustomLogs/archive/${date:yyyy-MM}/${ctx:LOGINID}-%d{MM-dd-yyyy}-%i.txt.gz">
                        <PatternLayout>
                            <Pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} %-5level %t %msg%xEx%n</Pattern>
                        </PatternLayout>
                        <Policies>
                            <TimeBasedTriggeringPolicy interval="6" modulate="true"/>
                            <SizeBasedTriggeringPolicy size="10 MB"/>
                        </Policies>
                        <!-- <DefaultRolloverStrategy max="100"/> -->
                    </RollingFile>
                </Route>
            </Routes>
        </Routing>
        <Async name="async" bufferSize="1000" includeLocation="true">
            <AppenderRef ref="AppRouting"/>
        </Async>
    </Appenders>
    <Loggers>
        <Root level="trace">
            <!--<AppenderRef ref="file" level="DEBUG"/> -->
            <AppenderRef ref="async"/>
            <!-- Uncomment the following if you want the log to be printed in weblogic console -->
             <AppenderRef ref="STDOUT"/> 
        </Root>
    </Loggers>
</Configuration>

      

I am trying to create multiple loggers with a different log layer for each, but nothing seems to work.

It would be great if you could give me some pointers or hints.

+3


source to share


1 answer


One of our client's requirements is to have different log levels for different registered users, as well as dynamically change log levels for the user. In addition, the administrator must have a control to stop all logging.

This will require a separate logger for each user, which I assume for each Thread

. Obviously, you cannot configure all of these loggers in advance, since you do not know how many users you will have at runtime. Therefore, you need to dynamically create loggers at runtime with calls to the log4j2 APIs. Details can be found in the log4j2 manual .

In addition, you need to create administrative controls that will allow you to access the log4j2 configuration and provide the ability to change the configuration at runtime.

Here's a small example of how you can achieve the goal of having one logger per logger Thread

and how to change the log level at runtime:

Here is the log4j2.xml config file:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <Console name="STDOUT" target="SYSTEM_OUT">
            <PatternLayout pattern="%m%n" />
        </Console>
    </Appenders>
    <Loggers>
        <Logger name="example" level="trace">
            <AppenderRef ref="STDOUT" />
        </Logger>
        <Root level="WARN">
            <AppenderRef ref="STDOUT" />
        </Root>
    </Loggers>
</Configuration>

      



Here is the java code:

package example;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.AppenderRef;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;

public class LogLvlByThreadMain {

    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable(){

            public void run() {
                addLogger(Thread.currentThread().getName());

                Logger log = LogManager.getLogger(Thread.currentThread().getName());
                log.info("here the first thread");
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                log.debug("some debug in first thread");
                log.info("finishing first thread");
            }}, "Thread1");

        Thread t2 = new Thread(new Runnable(){

            public void run() {
                addLogger(Thread.currentThread().getName());

                Logger log = LogManager.getLogger(Thread.currentThread().getName());
                log.info("here the second thread");

                changeLogLevel(Thread.currentThread().getName(), Level.DEBUG);

                log.debug("some debug in second thread");
            }}, "Thread2");

        t1.start();
        t2.start();
    }

    public static synchronized void addLogger(String name){
        AppenderRef ref = AppenderRef.createAppenderRef("STDOUT", null, null);
        AppenderRef[] refs = new AppenderRef[] {ref};

        LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
        Configuration config = ctx.getConfiguration();
        LoggerConfig loggerConfig = LoggerConfig.createLogger("false", Level.INFO, name,
                "true", refs, null, config, null );

        loggerConfig.addAppender(config.getAppender("STDOUT"), null, null);
        config.addLogger(name, loggerConfig);
        ctx.updateLoggers();
    }

    public static synchronized void changeLogLevel(String loggerName, Level lvl){
        LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
        Configuration config = ctx.getConfiguration();
        LoggerConfig loggerCfg = config.getLoggerConfig(loggerName);
        loggerCfg.setLevel(lvl);
        ctx.updateLoggers();
    }

}

      

Note that you have to think about the problems of synchronization with multiple streams, changing the configuration of logging "on the fly" - note how the two methods above, which change the configuration: synchronized

.

Output from the above code:

here the second thread
here the first thread
some debug in second thread
finishing first thread

      

This proves that the two threads have different log levels, because if we changed both of them, we would see two "debug" logs from more than just the second thread.

0


source







All Articles