roov
roov

Reputation: 33

How to update ${ctx:key} to achieve dynamic file name without executing log4j2 reconfigure()

Now I have problem with log4j2.

My goal is: Determine which file to log according to the value of "fileName" in the MDC map.

For example

public class A {
    
    void test() {
        //log in A.log
        MDC.put("fileName", A);
        
        //LoggerContext ctx = (LoggerContext)LoggerContext.getContext(false);
        //ctx.reconfigure();
        
        //Now, I want it log in A.log
        log.info("aaa");
        C c = new C();
        c.test();       
    }
    
}

public class B {
    
    void test() {
        //log in B.log
        MDC.put("fileName", B);
    
        //LoggerContext ctx = (LoggerContext)LoggerContext.getContext(false);
        //ctx.reconfigure();
        //Now, I want it log in B.log
        log.info("bbb");
        C c = new C();
        c.test();
    }

}

public class C {
    
    void test() {
        log.info("cccc");
    }   
    
}   

and my log4j2.xml is

<RollingFile name="rollingFile" fileName="/home/logs/${ctx:key}.log"
             filePattern="/home/logs/application-${ctx:key}-i.log">
    <PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n"/>
    <Policies>
        <SizeBasedTriggeringPolicy size="2MB"/>
    </Policies>
    <DefaultRolloverStrategy max="10"/>
</RollingFile>

if I uncomment reconfigure(), it works that When executing A.test(), "aaa", "cccc" log in A.log and when executing B.test(), "bbb", "cccc" log in B.log.

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

But considering the high concurrency scenario, I am worried that frequent updates will cause performance problems such as IO resources.

In configurtion of log4j2, we can use %X{key} to get MDC map value in real time.

So is it possible that we do not need to execute reconfigure() every time when MDC map values change?

or is there any way to achieve my goal?

Upvotes: 0

Views: 434

Answers (1)

rgoers
rgoers

Reputation: 9151

You want to use the RoutingAppender. You would use it as

<Routing name="Routing">
  <Routes pattern="$${ctx:fileName}">
    <Route>
      <RollingFile name="rollingFile" fileName="/home/logs/${ctx:fileName}.log"
         filePattern="/home/logs/application-${ctx:fileName}-i.log">
        <PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n"/>
        <SizeBasedTriggeringPolicy size="2MB"/>
        <DefaultRolloverStrategy max="10"/>
      </RollingFile>
    </Route>
  <Routes>
<Routing>

Note that this only configures a default Route so if fileName is not provided you will end up logging to a file literally named ${ctx:fileName}-n.log.

You also need to know that this should only be used when the possible values for fileName is limited. But if they are all known beforehand it is really better to directly declare them. If fileName is not well controlled you could end up running out of file handles.

Upvotes: 1

Related Questions