Log4j 2
  1. Log4j 2
  2. LOG4J2-314

How to create multiple appenders at runtime, sharing an AsyncAppender?

    Details

    • Type: Question Question
    • Status: Resolved
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: None
    • Fix Version/s: None
    • Component/s: Appenders, Configurators
    • Labels:
      None

      Description

      I have a log4j2.xml configuration file from which I am getting the Logger, attached is the snapshot. when my logger is created a new thread is created.

      Programmatically,I create a RollingFileAppender and attach the it to AsyncAppender . I attach the Async Appender to the configuration of the log4j.xml, And when I start the Async Appender again a new thread is created.

      Is this a bug? because I get a logger a new thread is generated and when I create a async appender another thread is created and both are doing the same job.

      Also there is already a asyncappender in my log4j2.xml and I am creating one via program. Will this cause a problem? I did this because I am currently unable to hook my programmatically created appender to the AsyncAppender defined in log4j2.xml.

      1. TestAppender.zip
        736 kB
        Sudharma Puranik
      2. log4j2.xml
        2 kB
        Sudharma Puranik
      3. loggerThread.png
        44 kB
        Sudharma Puranik
      4. asyncAppendThread.png
        23 kB
        Sudharma Puranik

        Activity

        Hide
        Sudharma Puranik added a comment -

        attached is the log4j2.xml file

        Show
        Sudharma Puranik added a comment - attached is the log4j2.xml file
        Hide
        Remko Popma added a comment - - edited

        Thanks! Can you also show how you programmatically create the logging components? A small program that demonstrates the issue would be ideal.

        The problem description above mentions that two threads are created, but that by itself is not the problem, right? I remember that in the email to the list you mentioned that your log messages were duplicated in the log files (so you log 5 messages and you get 10 messages of output in the log file).

        If my understanding is correct, could you update the problem description?

        Show
        Remko Popma added a comment - - edited Thanks! Can you also show how you programmatically create the logging components? A small program that demonstrates the issue would be ideal. The problem description above mentions that two threads are created, but that by itself is not the problem, right? I remember that in the email to the list you mentioned that your log messages were duplicated in the log files (so you log 5 messages and you get 10 messages of output in the log file). If my understanding is correct, could you update the problem description?
        Hide
        Sudharma Puranik added a comment -

        Sure, I would add it in some time.

        Show
        Sudharma Puranik added a comment - Sure, I would add it in some time.
        Hide
        Remko Popma added a comment -

        Sudharma, I appreciate you taking the time to create a small program that demonstrates the issue.
        If you really need to get this fixed quickly you can always submit a patch. That said, I will try to work on this this weekend, but no guarantees. (We're all volunteers working on this in our spare time.)

        I did take a look at your code. Are trying to do the following? (please correct me if I'm wrong)

        • you want to create Loggers dynamically at runtime
        • each Logger logs to its own file, using a RollingFileAppender
        • there is a RequestId string that needs to be embedded in the file name (I assume this string is unique)
        • you would like to use asynchronous logging so you wrap the RollingFileAppender in an AsyncAppender and connect the AsyncAppender to the LoggerConfig before returning the new Logger

        You may be able to get this to work (I haven't tried running your code yet), but how many Loggers do you expect will be created? If this is several 100s or even 1000s you will also end up with 1000s of threads: every AsyncAppender has its own thread. This approach will not scale very well.

        I'm beginning to think you need a different solution. I think we should consider providing a multiplexing appender that logs to separate files, perhaps based on key-values pairs you can set in the ThreadContextMap. This would be similar to the SiftingAppender that LogBack provides.

        Show
        Remko Popma added a comment - Sudharma, I appreciate you taking the time to create a small program that demonstrates the issue. If you really need to get this fixed quickly you can always submit a patch. That said, I will try to work on this this weekend, but no guarantees. (We're all volunteers working on this in our spare time.) I did take a look at your code. Are trying to do the following? (please correct me if I'm wrong) you want to create Loggers dynamically at runtime each Logger logs to its own file, using a RollingFileAppender there is a RequestId string that needs to be embedded in the file name (I assume this string is unique) you would like to use asynchronous logging so you wrap the RollingFileAppender in an AsyncAppender and connect the AsyncAppender to the LoggerConfig before returning the new Logger You may be able to get this to work (I haven't tried running your code yet), but how many Loggers do you expect will be created? If this is several 100s or even 1000s you will also end up with 1000s of threads: every AsyncAppender has its own thread. This approach will not scale very well. I'm beginning to think you need a different solution. I think we should consider providing a multiplexing appender that logs to separate files, perhaps based on key-values pairs you can set in the ThreadContextMap. This would be similar to the SiftingAppender that LogBack provides.
        Hide
        Scott Deboy added a comment -

        Thread pool support would also be a reasonable implementation option here to avoid the scaling issue.

        Show
        Scott Deboy added a comment - Thread pool support would also be a reasonable implementation option here to avoid the scaling issue.
        Hide
        Ralph Goers added a comment -

        The RoutingAppender provides multiplexing and dynamic appender creation. But it may not completely solve for this use case.

        Show
        Ralph Goers added a comment - The RoutingAppender provides multiplexing and dynamic appender creation. But it may not completely solve for this use case.
        Hide
        Sudharma Puranik added a comment -

        Remko Popma : Yes surely If I have the eligibility I can try to submit the Patch. Also about my usecase,

        Its not several threads , It is just that I would want to do a physical dump with given request Id for tracing. Its not a frequent task.

        Regarding the AsyncAppender , I thought of having a single AsyncAppender to a log4j Configuration and we can add all other Appender to it but as of now there is no facility as such. We can avoid having multiple AsyncAppenders.

        I can check with the ThreadContextMap.

        Show
        Sudharma Puranik added a comment - Remko Popma : Yes surely If I have the eligibility I can try to submit the Patch. Also about my usecase, Its not several threads , It is just that I would want to do a physical dump with given request Id for tracing. Its not a frequent task. Regarding the AsyncAppender , I thought of having a single AsyncAppender to a log4j Configuration and we can add all other Appender to it but as of now there is no facility as such. We can avoid having multiple AsyncAppenders. I can check with the ThreadContextMap.
        Hide
        Remko Popma added a comment -

        Scott Deboy: One thread (AsyncLoggers or AsyncAppender) should be fine, as long as on the back end, the message ends up in the correct log file. It also should not be necessary to create special Logger objects.

        Ralph Goers: Thanks for the pointer to the RoutingAppender!

        Sudharma Puranik: I was guessing that you were trying to connect your custom log configuration code to the XML configuration in order to re-use the AsyncAppender defined in the XML.

        I think there is a simpler way to do what you want to do that does not involve any custom log configuration code. You should be able to get rid of the NAPLogger class and just use XML configuration. I'm thinking the application code can look like this:

        // No need to create/configure a custom "dumpLogger" in your program: just use a standard Logger 
        private static final Logger logger = LogManager.getLogger(MyClass.class);
        
        private void someMethod(String requestId) {
            ThreadContext.put("requestId", requestId);
            logger.info("This message is written to a new file with requestId in the file name");
            logger.info("Another message written to that same file");
            
            ThreadContext.remove(requestId);
            logger.info("This message is written to the default log file");
        }
        

        Sudharma, would this solve your problem? Note: with this solution you cannot set the MAX-SIZE for rollover...

        It may take a bit of experimenting with the XML configuration to set up a RoutingAppender that creates a new file for each unique requestId. The pattern that you would use with the above example is $${ctx:requestId}.

        Here is an example configuration file. (I haven't tried this but it may help get you started.)

        <?xml version="1.0" encoding="UTF-8"?>
        <configuration status="DEBUG" name="MyApp" packages="">
          <appenders>
            <!-- SUMMARY FILE APPENDER -->
            <RollingFile name="SUMMARY_ALL" fileName="./logs/css-summary.log"
                filePattern="logs/$${date:yyyy-MM}/summary-%d{yyyy-MM-dd-HH}-%i.log.gz">
              <PatternLayout>
                <pattern>%d{ISO8601} [%t] %p %c %L - %m%n</pattern>
              </PatternLayout>
              <Policies>
                <TimeBasedTriggeringPolicy interval="6" modulate="true" />
                <SizeBasedTriggeringPolicy size="50 MB" />
              </Policies>
            </RollingFile>
        
            <!-- Routing sends messages to a separate dump file if ThreadContext has key "requestId",
                 other events are sent to the SUMMARY_ALL log file. -->
            <Routing name="Routing">
              <Routes pattern="$${ctx:requestId}">
                <Route>
                  <RollingFile name="Rolling-${ctx:requestId}" fileName="logs/dump-${ctx:requestId}.log"
                               filePattern="./logs/${date:yyyy-MM}/${ctx:requestId}-dump-%d{yyyy-MM-dd}-%i.log">
                    <PatternLayout>
                      <pattern>%d{ISO8601} [%t] %p %c %L - %m%n</pattern>
                    </PatternLayout>
                    <SizeBasedTriggeringPolicy size="500 MB" />
                  </RollingFile>
                </Route>
                <Route appender-ref="SUMMARY_ALL" key="Summary"/>
              </Routes>
            </Routing>
        
            <!-- Async appender wraps the Routing appender, so all logging is asynchronous. -->
            <Async name="Async" bufferSize="256">
              <appender-ref ref="Routing" />
            </Async>
          </appenders>
        
          <loggers>
            <root level="DEBUG">
              <appender-ref ref="Async" />
            </root>
          </loggers>
        </configuration>
        
        Show
        Remko Popma added a comment - Scott Deboy : One thread (AsyncLoggers or AsyncAppender) should be fine, as long as on the back end, the message ends up in the correct log file. It also should not be necessary to create special Logger objects. Ralph Goers : Thanks for the pointer to the RoutingAppender! Sudharma Puranik : I was guessing that you were trying to connect your custom log configuration code to the XML configuration in order to re-use the AsyncAppender defined in the XML. I think there is a simpler way to do what you want to do that does not involve any custom log configuration code. You should be able to get rid of the NAPLogger class and just use XML configuration. I'm thinking the application code can look like this: // No need to create/configure a custom "dumpLogger" in your program: just use a standard Logger private static final Logger logger = LogManager.getLogger(MyClass.class); private void someMethod( String requestId) { ThreadContext.put( "requestId" , requestId); logger.info( "This message is written to a new file with requestId in the file name" ); logger.info( "Another message written to that same file" ); ThreadContext.remove(requestId); logger.info( "This message is written to the default log file" ); } Sudharma, would this solve your problem? Note: with this solution you cannot set the MAX-SIZE for rollover... It may take a bit of experimenting with the XML configuration to set up a RoutingAppender that creates a new file for each unique requestId. The pattern that you would use with the above example is $${ctx:requestId}. Here is an example configuration file. (I haven't tried this but it may help get you started.) <?xml version= "1.0" encoding= "UTF-8" ?> <configuration status= "DEBUG" name= "MyApp" packages=""> <appenders> <!-- SUMMARY FILE APPENDER --> <RollingFile name= "SUMMARY_ALL" fileName= "./logs/css-summary.log" filePattern= "logs/$${date:yyyy-MM}/summary-%d{yyyy-MM-dd-HH}-%i.log.gz" > <PatternLayout> <pattern>%d{ISO8601} [%t] %p %c %L - %m%n</pattern> </PatternLayout> <Policies> <TimeBasedTriggeringPolicy interval= "6" modulate= " true " /> <SizeBasedTriggeringPolicy size= "50 MB" /> </Policies> </RollingFile> <!-- Routing sends messages to a separate dump file if ThreadContext has key "requestId" , other events are sent to the SUMMARY_ALL log file. --> <Routing name= "Routing" > <Routes pattern= "$${ctx:requestId}" > <Route> <RollingFile name= "Rolling-${ctx:requestId}" fileName= "logs/dump-${ctx:requestId}.log" filePattern= "./logs/${date:yyyy-MM}/${ctx:requestId}-dump-%d{yyyy-MM-dd}-%i.log" > <PatternLayout> <pattern>%d{ISO8601} [%t] %p %c %L - %m%n</pattern> </PatternLayout> <SizeBasedTriggeringPolicy size= "500 MB" /> </RollingFile> </Route> <Route appender-ref= "SUMMARY_ALL" key= "Summary" /> </Routes> </Routing> <!-- Async appender wraps the Routing appender, so all logging is asynchronous. --> <Async name= "Async" bufferSize= "256" > <appender-ref ref= "Routing" /> </Async> </appenders> <loggers> <root level= "DEBUG" > <appender-ref ref= "Async" /> </root> </loggers> </configuration>
        Hide
        Sudharma Puranik added a comment -

        Hi Remko Popma : thank you for your reply, I was about to do the same. let me try and update you..

        Show
        Sudharma Puranik added a comment - Hi Remko Popma : thank you for your reply, I was about to do the same. let me try and update you..
        Hide
        Sudharma Puranik added a comment -

        Remko Popma : Yes I am able to attain my routing in some way. Thank you.

        Also b.t.w can I have multiple AsyncAppenders ? does it have any side effect?

        Show
        Sudharma Puranik added a comment - Remko Popma : Yes I am able to attain my routing in some way. Thank you. Also b.t.w can I have multiple AsyncAppenders ? does it have any side effect?
        Hide
        Remko Popma added a comment -

        It is possible to have multiple AsyncAppenders. Be careful though, in the example above, RoutingAppender has an appender-ref that points to SUMMARY_ALL, so you should not have a separate AsyncAppender that also refers to this SUMMARY_ALL.

        Another drawback is that every AsyncAppender will start its own thread, but this is probably not a huge problem.

        An alternative would be to use AsyncLoggers (http://logging.apache.org/log4j/2.0/manual/async.html). Just remove all AsyncAppenders from the config, and set system property -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector. This makes the configuration simpler, uses only one thread, and is probably faster too.

        Show
        Remko Popma added a comment - It is possible to have multiple AsyncAppenders. Be careful though, in the example above, RoutingAppender has an appender-ref that points to SUMMARY_ALL, so you should not have a separate AsyncAppender that also refers to this SUMMARY_ALL. Another drawback is that every AsyncAppender will start its own thread, but this is probably not a huge problem. An alternative would be to use AsyncLoggers ( http://logging.apache.org/log4j/2.0/manual/async.html ). Just remove all AsyncAppenders from the config, and set system property -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector . This makes the configuration simpler, uses only one thread, and is probably faster too.
        Hide
        Remko Popma added a comment - - edited

        Can I change the title of this Jira?
        I think the below may describe the issue better (and may help others with the same problem):

        How to create multiple appenders at runtime, sharing an AsyncAppender?

        Also, can we mark this issue as resolved?

        Show
        Remko Popma added a comment - - edited Can I change the title of this Jira? I think the below may describe the issue better (and may help others with the same problem): How to create multiple appenders at runtime, sharing an AsyncAppender? Also, can we mark this issue as resolved?
        Hide
        Sudharma Puranik added a comment - - edited

        For routing I can make for specific Summary file

        <Route appender-ref="SUMMARY_ALL" key="Summary"/>

        but the problem is , I have to add into the ThreadContext with requestID --> Summary. So instead what I decided was that let the default Routing be the one with requestId and I moved

         <appender-ref ref="SUMMARY_ALL" />

        into my Async Appender. Only the problem with this is that once I remove the requestId from the context, the next logging creates an empty file with *dump-${ctx* .

        Can we have a feasibility that any key removed from ThreadContext should internally stop routing If any further logging? Because Default also will expect something as value.

        And you can mark this as resolved. Thanks a lot for your help.

        Show
        Sudharma Puranik added a comment - - edited For routing I can make for specific Summary file <Route appender-ref="SUMMARY_ALL" key="Summary"/> but the problem is , I have to add into the ThreadContext with requestID --> Summary. So instead what I decided was that let the default Routing be the one with requestId and I moved <appender-ref ref="SUMMARY_ALL" /> into my Async Appender. Only the problem with this is that once I remove the requestId from the context, the next logging creates an empty file with * dump-${ctx * . Can we have a feasibility that any key removed from ThreadContext should internally stop routing If any further logging? Because Default also will expect something as value. And you can mark this as resolved. Thanks a lot for your help.
        Hide
        Remko Popma added a comment -

        I'm not that familiar with how the RoutingAppender works...
        Basically you want: (please correct me if I'm wrong)

        • log events should be logged to a special file if and only if the ThreadContextMap has some value for key "requestId".
        • if the ThreadContextMap is empty (or does not have a value for key "requestId") the event should be logged to the SUMMARY_ALL appender

        I think this should be possible with just minor tweaks to the config I proposed above.
        Can you try defining the default route (to SUMMARY_ALL) without a key?

        Like this:

            <Routing name="Routing">
              <Routes pattern="$${ctx:requestId}">
                <Route>
                  <RollingFile name="Rolling-${ctx:requestId}" ... (as above)
                      ...
                  </RollingFile>
                </Route>
                <Route appender-ref="SUMMARY_ALL" /> <!-- no key -->
              </Routes>
            </Routing>
        

        If that does not work, can you try key="ROUTING_APPENDER_DEFAULT"?

        Like this:

            <Routing name="Routing">
              <Routes pattern="$${ctx:requestId}">
                <Route>
                  <RollingFile name="Rolling-${ctx:requestId}" ... (as above)
                      ...
                  </RollingFile>
                </Route>
                <Route appender-ref="SUMMARY_ALL" key="ROUTING_APPENDER_DEFAULT"/>
              </Routes>
            </Routing>
        
        Show
        Remko Popma added a comment - I'm not that familiar with how the RoutingAppender works... Basically you want: (please correct me if I'm wrong) log events should be logged to a special file if and only if the ThreadContextMap has some value for key "requestId". if the ThreadContextMap is empty (or does not have a value for key "requestId") the event should be logged to the SUMMARY_ALL appender I think this should be possible with just minor tweaks to the config I proposed above. Can you try defining the default route (to SUMMARY_ALL) without a key? Like this: <Routing name= "Routing" > <Routes pattern= "$${ctx:requestId}" > <Route> <RollingFile name= "Rolling-${ctx:requestId}" ... (as above) ... </RollingFile> </Route> <Route appender-ref= "SUMMARY_ALL" /> <!-- no key --> </Routes> </Routing> If that does not work, can you try key="ROUTING_APPENDER_DEFAULT" ? Like this: <Routing name= "Routing" > <Routes pattern= "$${ctx:requestId}" > <Route> <RollingFile name= "Rolling-${ctx:requestId}" ... (as above) ... </RollingFile> </Route> <Route appender-ref= "SUMMARY_ALL" key= "ROUTING_APPENDER_DEFAULT" /> </Routes> </Routing>
        Hide
        Sudharma Puranik added a comment -

        1st doesnt work because there can be only 1 default Routing and 2nd one I can do this but always I have to add ROUTING_APPENDER_DEFAULT to ThreadContext which I dont want , So my proposal was that, If possible log4j can internally handle the DEFAULT to the root logger If I dont specify any.

        I really appreciate your effort for helping me to solve my problem. Thank you very much for your dedication and interest to solve the problems I love your log4j Team for your prompt response for any trivial issue.

        You folks are doing great work :-)Keep it up guys.

        Show
        Sudharma Puranik added a comment - 1st doesnt work because there can be only 1 default Routing and 2nd one I can do this but always I have to add ROUTING_APPENDER_DEFAULT to ThreadContext which I dont want , So my proposal was that, If possible log4j can internally handle the DEFAULT to the root logger If I dont specify any. I really appreciate your effort for helping me to solve my problem. Thank you very much for your dedication and interest to solve the problems I love your log4j Team for your prompt response for any trivial issue. You folks are doing great work :-)Keep it up guys.
        Hide
        Remko Popma added a comment -

        I don't understand why the 1st does not work.
        My intention was to make <Route appender-ref="SUMMARY_ALL" /> <!-- no key --> the default routing so that you don't need to put anything in the ThreadContextMap...
        Can you explain what does not work?

        Show
        Remko Popma added a comment - I don't understand why the 1st does not work. My intention was to make <Route appender-ref="SUMMARY_ALL" /> <!-- no key --> the default routing so that you don't need to put anything in the ThreadContextMap... Can you explain what does not work?
        Hide
        Remko Popma added a comment - - edited

        Oops... I see. If a route does not have a key it becomes the default.
        How about this then:

            <Routing name="Routing">
              <Routes pattern="$${ctx:requestId}">
                <Route key="non-default"> <!-- any key, to make sure this is not the default -->
                  <RollingFile name="Rolling-${ctx:requestId}" ... (as above)
                      ...
                  </RollingFile>
                </Route>
                <Route appender-ref="SUMMARY_ALL" /> <!-- no key, this is the default route -->
              </Routes>
            </Routing>
        
        Show
        Remko Popma added a comment - - edited Oops... I see. If a route does not have a key it becomes the default. How about this then: <Routing name= "Routing" > <Routes pattern= "$${ctx:requestId}" > <Route key= "non- default " > <!-- any key, to make sure this is not the default --> <RollingFile name= "Rolling-${ctx:requestId}" ... (as above) ... </RollingFile> </Route> <Route appender-ref= "SUMMARY_ALL" /> <!-- no key, this is the default route --> </Routes> </Routing>
        Hide
        Remko Popma added a comment -

        Changed title (was: Multiple thread creation problem with AsyncAppender).

        Show
        Remko Popma added a comment - Changed title (was: Multiple thread creation problem with AsyncAppender).
        Hide
        Sudharma Puranik added a comment - - edited

        check this example probably you will understand how Routing works.

        public static void main(String[] args) {
        		ThreadContext.put("fruits", "apple");
        		logger.info("This message is written to a new file with requestId in the file name");
        		logger.info("Another message written to that same file");
        
        		for (int i = 0; i <= 5000; i++) {
        			logger.debug(" Loop 1 " + i);
        		}
        //		ThreadContext.remove("requestId");
        
        		 ThreadContext.put("fruits", "orange");
        		logger.info("This message is written to a new file with requestId in the file name");
        		logger.info("Another message written to that same file");
        
        		logger.debug("Sudharma Tested");
        		for (int i = 0; i <= 500000; i++) {
        			logger.debug("Loop 1 " + i);
        		}
        		logger.info("This message is written to the default log file");
        
        	}
        

        log4j2.xml

        <Routing name="Routing">
        			<Routes pattern="$${ctx:fruits}">
        				<Route key="apple">
        					<RollingFile name="Rolling-${ctx:fruits}" fileName="logs/apple-${ctx:fruits}.log"
        						filePattern="./logs/${date:yyyy-MM}/${ctx:fruits}-apple-%d{yyyy-MM-dd}-%i.log.gz">
        						<PatternLayout>
        							<pattern>%d{ISO8601} [%t] %p %c{3} %L - %m%n</pattern>
        						</PatternLayout>
        						<Policies>
        							<TimeBasedTriggeringPolicy interval="6"
        								modulate="true" />
        							<SizeBasedTriggeringPolicy size="10 MB" />
        						</Policies>
        					</RollingFile>
        				</Route>
        				<Route>
        					<RollingFile name="Rolling-${ctx:fruits}" fileName="logs/other-${ctx:fruits}.log"
        						filePattern="./logs/${date:yyyy-MM}/${ctx:fruits}-other-%d{yyyy-MM-dd}-%i.log.gz">
        						<PatternLayout>
        							<pattern>%d{ISO8601} [%t] %p %c{3} %L - %m%n</pattern>
        						</PatternLayout>
        						<Policies>
        							<TimeBasedTriggeringPolicy interval="6"
        								modulate="true" />
        							<SizeBasedTriggeringPolicy size="10 MB" />
        						</Policies>
        					</RollingFile>
        				</Route>
        			</Routes>
        		</Routing>
        

        Fruits with Apple go to apple log, Now for orange its not defined so it will go to others, the point is that whatever you route you have to add it to ThreadContext, even default.

        Show
        Sudharma Puranik added a comment - - edited check this example probably you will understand how Routing works. public static void main(String[] args) { ThreadContext.put("fruits", "apple"); logger.info("This message is written to a new file with requestId in the file name"); logger.info("Another message written to that same file"); for (int i = 0; i <= 5000; i++) { logger.debug(" Loop 1 " + i); } // ThreadContext.remove("requestId"); ThreadContext.put("fruits", "orange"); logger.info("This message is written to a new file with requestId in the file name"); logger.info("Another message written to that same file"); logger.debug("Sudharma Tested"); for (int i = 0; i <= 500000; i++) { logger.debug("Loop 1 " + i); } logger.info("This message is written to the default log file"); } log4j2.xml <Routing name="Routing"> <Routes pattern="$${ctx:fruits}"> <Route key="apple"> <RollingFile name="Rolling-${ctx:fruits}" fileName="logs/apple-${ctx:fruits}.log" filePattern="./logs/${date:yyyy-MM}/${ctx:fruits}-apple-%d{yyyy-MM-dd}-%i.log.gz"> <PatternLayout> <pattern>%d{ISO8601} [%t] %p %c{3} %L - %m%n</pattern> </PatternLayout> <Policies> <TimeBasedTriggeringPolicy interval="6" modulate="true" /> <SizeBasedTriggeringPolicy size="10 MB" /> </Policies> </RollingFile> </Route> <Route> <RollingFile name="Rolling-${ctx:fruits}" fileName="logs/other-${ctx:fruits}.log" filePattern="./logs/${date:yyyy-MM}/${ctx:fruits}-other-%d{yyyy-MM-dd}-%i.log.gz"> <PatternLayout> <pattern>%d{ISO8601} [%t] %p %c{3} %L - %m%n</pattern> </PatternLayout> <Policies> <TimeBasedTriggeringPolicy interval="6" modulate="true" /> <SizeBasedTriggeringPolicy size="10 MB" /> </Policies> </RollingFile> </Route> </Routes> </Routing> Fruits with Apple go to apple log, Now for orange its not defined so it will go to others, the point is that whatever you route you have to add it to ThreadContext, even default.
        Hide
        Remko Popma added a comment -

        I see...

        If you don't call ThreadContext.put("requestId", "Summary") for every thread, but leave the ThreadContext empty instead, you get an empty *dump-${ctx* log file...

        Hmm, not ideal. Better than having the custom NAPLogger, but not perfect either...
        I think this should maybe be in a separate JIRA as a request to improve the RoutingAppender.

        Show
        Remko Popma added a comment - I see... If you don't call ThreadContext.put("requestId", "Summary") for every thread, but leave the ThreadContext empty instead, you get an empty * dump-${ctx * log file... Hmm, not ideal. Better than having the custom NAPLogger, but not perfect either... I think this should maybe be in a separate JIRA as a request to improve the RoutingAppender.
        Hide
        Sudharma Puranik added a comment -

        hmm.. I think as of now I can go ahead but I am really counting because we have major GA in Jan 2014. Hope log4j 2 will stabilize eventually.

        Show
        Sudharma Puranik added a comment - hmm.. I think as of now I can go ahead but I am really counting because we have major GA in Jan 2014. Hope log4j 2 will stabilize eventually.
        Hide
        Remko Popma added a comment -

        OK, I will mark this Jira ticket as resolved.

        Please remember, if you need features or fixes for the GA you need to raise Jira tickets for them.
        Thanks for the feedback!

        Show
        Remko Popma added a comment - OK, I will mark this Jira ticket as resolved. Please remember, if you need features or fixes for the GA you need to raise Jira tickets for them. Thanks for the feedback!
        Hide
        Sudharma Puranik added a comment -

        Ok Thank you Remko Popma. I will do the same.

        Show
        Sudharma Puranik added a comment - Ok Thank you Remko Popma . I will do the same.

          People

          • Assignee:
            Unassigned
            Reporter:
            Sudharma Puranik
          • Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development