log4j java - dynamic logger creation in multi-thread environment

26 Views Asked by At

I am trying to incorporate a new logger in my application, I already have a legacy xml file that looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" packages="logger">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <ThresholdFilter level="TRACE" onMatch="ACCEPT" onMismatch="DENY"/> <!--change the level to determine the log level filter-->
            <PatternLayout pattern="%highlight{%d{HH:mm:ss.SSS} %pid |%t| [%class{1}] (%M) {%X{autoTesterTask}} %-5level : %L - %msg%xEx%n}{STYLE=DEFAULT}" disableAnsi="false" />
        </Console>
        <RollingFile name="log">
            <FileName>C:/qa/TestCaseGenerator/SystemConsole.log</FileName>
            <FilePattern>C:/qa/TestCaseGenerator/SystemConsoleLogs/%d{yyyy-MM-dd-hh-mm}_SystemConsole.zip</FilePattern>
            <PatternLayout pattern="%d{HH:mm:ss.SSS} %pid |%t| [%class{1}] (%M) {%X{autoTesterTask}} %-5level : %L - %msg%xEx%n"/>
            <Policies>
                <SizeBasedTriggeringPolicy size="512 MB"/>
            </Policies>
            <DefaultRolloverStrategy max="20"/>
        </RollingFile>
    </Appenders>
    <Loggers>
        <Logger name="log4j.test.Log4jTest" level="trace">
            <AppenderRef ref="log"/>
            <AppenderRef ref="Console"/>
        </Logger>
        <Root level="trace">   <!--choose between log/Console-->
            <AppenderRef ref="log"/>
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>

I tried using this and adding another appender and logger to the xml file, this is my apender:

 <Routing name="TaskLoggerManager">
            <Routes pattern="$${ctx:autoTesterTask}">
                <!-- Define routes based on thread context (task) -->
                <Route>
                    <RollingFile name="taskRolling"
                                 fileName="${sys:taskLogFilePath_${ctx:autoTesterTask}}.log"
                                 filePattern="${sys:taskLogFilePath_${ctx:autoTesterTask}}-%i.log">
                        <PatternLayout pattern="%d{HH:mm:ss.SSS} %msg%xEx%n"/>
                        <Policies>
                            <SizeBasedTriggeringPolicy size="10 MB"/>
                        </Policies>
                    </RollingFile>
                </Route>
            </Routes>
        </Routing>

I am using ThreadContext to set the autoTesterTask value, but from what I understood in a multithreaded environment using the ThreadContext and the logger.info(msg) needs to be atomic meaning I cannot log anything until one thread is finished due to the fact that it needs to change the ThreadContext and then log, if another log will change the ThreadContext while the first log did not finish running logger.info(msg) there could be a mismatch in data, right?

So I tried also progrematically creating a helper class to add appenders:

public class LoggerManager {
    private static final Logger logger = LogManager.getLogger();
    private final LoggerContext ctx;
    private final LoggerConfig loggerConfig;
    private HashMap<String,AppenderRef> refs;


    public static Logger getLogger() {
        return logger;
    }

    private static LoggerManager loggerManagerInstance;

    private LoggerManager(){
        ctx = (LoggerContext) LogManager.getContext(false);
        loggerConfig = ctx.getConfiguration().getRootLogger();
        refs = new HashMap<>();
    }

    public static LoggerManager LoggerManager(){
        if (loggerManagerInstance==null)
            loggerManagerInstance = new LoggerManager();
        return loggerManagerInstance;
    }



    public void removeLogger(String name){
        Appender appender = ctx.getConfiguration().getAppender(name);
        if(appender != null){
            appender.stop();
            loggerConfig.removeAppender(name);
            ctx.getConfiguration().getAppenders().remove(name);
            refs.remove(name);
            ctx.getConfiguration().removeLogger(name);
            ctx.updateLoggers();
        }

        try{
            TimeUnit.SECONDS.sleep(3);
        }catch (InterruptedException e){
            logger.error(e);
        }

        logger.traceExit();
    }

 public Logger getLogger(String name) {
        return ctx.getLogger(name);
    }

    public void createLogger(String name,String filePath){
        Configuration config = ctx.getConfiguration();

        PatternLayout layout = PatternLayout.newBuilder()
          .withConfiguration(config)
          .withPattern("%d{HH:mm:ss.SSS} %msg%xEx%n")
          .build();

        Appender appender = RollingFileAppender.newBuilder()
          .setConfiguration(config)
          .setName(name)
          .setLayout(layout)
          .withFileName(filePath + ".log")
          .withPolicy(SizeBasedTriggeringPolicy.createPolicy("2 KB"))
          .withFilePattern(filePath + "_%d{yyyy-MM-dd_hh-mm-ss}.log")
          .withStrategy(DefaultRolloverStrategy.newBuilder()
            .withConfig(config)
            .build())
          .withImmediateFlush(false)
          .build();

        appender.start();

        config.addAppender(appender);

        AppenderRef ref = AppenderRef.createAppenderRef(name, null, null);

        loggerConfig.addAppender(appender, null, null);

        refs.put(name,ref);

        ctx.updateLoggers();

    }
}

I get the files being created but multiple unrelated logs that comes I guess from my log4j2.xml root logger that is loading when application is up (which I still need I just want to add other loggers without interfering with the old ones).

Also, read about MDC and NDC which both still sounds like won't fix my thread issues or dynamically creating appenders and loggers issues.

Is there a way to have my log4j2.xml file still loading in the beginning of my application and also when some threads are being executed during my application runtime creating new loggers that will only serve these threads (appender per thread, each thread logs to different file) and once thread dies I don't need them anymore.

0

There are 0 best solutions below