-1

I would like to define 1 single Appender in my log4j2.xml configuration file, and using the magic of the Properties Substitution of Log4J2, be able to somehow log into 2 different files.

I imagine the Appender would look something like:

<RollingFile name="Rolling-${filename}" fileName="${filename}" filePattern="${filename}.%i.log.gz">
  <PatternLayout>
    <pattern>%d %p %c{1.} [%t] %m%n</pattern>
  </PatternLayout>
  <SizeBasedTriggeringPolicy size="500" />
</RollingFile>

Is there a way for a Logger to use this appender and to pass the filename property?

Or is there a way to pass it when we fetch the Logger with LogManager.getLogger?

Note that those logger may or may not be in the same Thread, it has to support both cases, so I don't think it's possible to use ThreadContext nor System properties.

benji
  • 2,331
  • 6
  • 33
  • 62

2 Answers2

0

The closest thing I can think of is RoutingAppender. RoutingAppender allows the log file to be dynamically selected based on values in some lookup. A popular built-in lookup is the ThreadContext map (see the example on the FAQ page), but you can create a custom lookup. Example code:

ThreadContext.put("ROUTINGKEY", "foo");
logger.debug("This message gets sent to route foo");

// Do some work, including logging by various loggers.
// All logging done in this thread is sent to foo.
// Other threads can also log to foo at the same time by setting ROUTINGKEY=foo.

logger.debug("... and we are done");
ThreadContext.remove("ROUTINGKEY"); // this thread no longer logs to foo

Example config that creates log files on the fly:

<Routing name="Routing">
  <Routes pattern="$${ctx:ROUTINGKEY}"> 
    <!-- This route is chosen if ThreadContext has a value for ROUTINGKEY.
         The value dynamically determines the name of the log file. -->
    <Route>
      <RollingFile name="Rolling-${ctx:ROUTINGKEY}" fileName="logs/other-${ctx:ROUTINGKEY}.log"
    filePattern="./logs/${date:yyyy-MM}/${ctx:ROUTINGKEY}-other-%d{yyyy-MM-dd}-%i.log.gz">
    <PatternLayout>
      <pattern>%d{ISO8601} [%t] %p %c{3} - %m%n</pattern>
    </PatternLayout>
    <Policies>
      <TimeBasedTriggeringPolicy interval="6" modulate="true" />
      <SizeBasedTriggeringPolicy size="10 MB" />
    </Policies>
      </RollingFile>
    </Route>
  </Routes>
    <!-- This route is chosen if ThreadContext has no value for key ROUTINGKEY. -->
    <Route key="$${ctx:ROUTINGKEY}">
      <RollingFile name="Rolling-default" fileName="logs/default.log"
    filePattern="./logs/${date:yyyy-MM}/default-%d{yyyy-MM-dd}-%i.log.gz">
        <PatternLayout>
      <pattern>%d{ISO8601} [%t] %p %c{3} - %m%n</pattern>
        </PatternLayout>
        <Policies>
          <TimeBasedTriggeringPolicy interval="6" modulate="true" />
          <SizeBasedTriggeringPolicy size="10 MB" />
        </Policies>
      </RollingFile>
    </Route>
</Routing>

An alternative is to configure multiple loggers, each pointing to a separate appender (with additivity="false"). This allows your application to control the destination file by obtaining a logger by its name. However, in that case you would need to configure separate appenders so this does not fulfill your requirement, I mention it for completeness.

Remko Popma
  • 35,130
  • 11
  • 92
  • 114
  • Yes I am definitely looking at the RoutingAppender. The thing is that ThreadContext will not allow me to declare 2 logger instances that use the same appender but log to different files in the same thread. – benji May 22 '15 at 18:06
  • I see... The example uses the ThreadContext map lookup, but RoutingAppender also works with other lookups. It should be possible to come up with a custom lookup for your use case. For example, here is a custom lookup based on markers: http://stackoverflow.com/questions/30111754/is-there-a-way-to-route-logs-based-on-marker-with-the-routingappender-in-log4j2/30266143#30266143 – Remko Popma May 23 '15 at 12:30
  • Since markers are based on LogEvents it doesn't possible to inform the location when you declare the Logger. I don't think there is a way to "attach" some info to the Logger. – benji May 26 '15 at 17:32
0

I am using the logger name to pass arguments to the appender.

It's hacky but it works:

LogManager.getLogger("com.company.test.Test.logto.xyz.log")

A custom StrLookup is necessary to extract the filename from the logger name.

benji
  • 2,331
  • 6
  • 33
  • 62