12

Here is the challenge I'm facing:

I have a servlet program. And I need to write logs for each user to the folder named after that user. Something like this:

    // stores message to David folder
    // /root_path/David/logfile.log
    logger.error(MarkerManager.getMarker("David"), "Error happened");

    // stores message to Mark folder
    // /root_path/Mark/logfile.log                 
    logger.error(MarkerManager.getMarker("Mark"), "Something is broken");

In my example I used markers. But I don't really know whether markers appropriate for this task.

In general my appender should behave like RollingRandomAccessFile appender. I guess config for appender must look like something like this:

     <RollingRandomAccessFile name="rollingFile"
                 fileName="logs/{markerName ?????}/movie-db.log"
                 filePattern="logs/log-%d{yyyy-MM-dd}-%i.log.zip"
                 append="false"
                 immediateFlush="false"
                 ignoreExceptions="true">
        <PatternLayout pattern="%d{ISO8601} %level{length=5} [%thread] %logger - %msg%n"/>
        <Policies>
            <SizeBasedTriggeringPolicy size="25 MB"/>
            <TimeBasedTriggeringPolicy />
        </Policies>
        <DefaultRolloverStrategy max="10"/>
    </RollingRandomAccessFile>

Any ideas?

Yurii Bondarenko
  • 3,460
  • 6
  • 28
  • 47

2 Answers2

31

Thanks to @Remko Popma answer I figure it out. Here is the solution example:

package com.bondarenko.tmp;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.ThreadContext;

public class TestRouting {

private final static Logger log = LogManager.getLogger(TestRouting.class);

public static void main(String[] args) {
    ThreadContext.put("logFileName", "David");
    log.info("Error happened");

    ThreadContext.put("logFileName", "Mark");
    log.info("Something is broken");

    ThreadContext.remove("logFileName");
}
}

And log4j.xml:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn">

<Appenders>
    <Console name="consoleAppender" target="SYSTEM_OUT">
        <!--SHORT PATTERN-->
        <PatternLayout pattern="%d{ABSOLUTE} %level{length=5} [%thread] %logger{1} - %msg%n"/>
        <!--ROBUST PATTERN
        <PatternLayout pattern="%d{ISO8601} %level{length=5} [%thread] %logger - %msg%n"/>-->
    </Console>

    <Routing name="RoutingAppender">
        <Routes pattern="${ctx:logFileName}">
            <Route>
                <RollingFile name="Rolling-${ctx:logFileName}"
                             fileName="logs/${ctx:logFileName}"
                             filePattern="logs/${ctx:logFileName}.%i.log.gz">
                    <PatternLayout pattern="%d{ABSOLUTE} %level{length=5} [%thread] %logger{1} - %msg%n"/>
                    <SizeBasedTriggeringPolicy size="512" />
                </RollingFile>
            </Route>

            <!-- By having this set to ${ctx:logFileName} it will match when filename
                 is not set in the context -->
            <Route ref="consoleAppender" key="${ctx:logFileName}"/>
        </Routes>
    </Routing>

</Appenders>

<Loggers>
    <Logger name="com.bondarenko.tmp" level="info" additivity="false">
        <AppenderRef ref="RoutingAppender"/>
    </Logger>
</Loggers>

Yurii Bondarenko
  • 3,460
  • 6
  • 28
  • 47
  • 2
    Thanks! You're amazing! :) – dwjohnston Aug 05 '16 at 00:05
  • 2
    Comparing your example to the FAQ page (https://logging.apache.org/log4j/2.0/faq.html#separate_log_files), it seems you need and not , right? – mac Nov 04 '16 at 07:15
  • 1
    Caveat: "Route", "ThreadContext" and friends are Log4j 2.0 features. You can't use them if you're stuck with an older version like Log4j 1.2 :( – paulsm4 Jan 25 '18 at 18:55
  • 1
    I'm getting an error :- `Unable to create file /var/lib/tomcat8/logs/business_system/${ctx:userId}/log.log java.io.IOException: The filename, directory name, or volume label syntax is incorrect` when using rollingFile name as - `/var/lib/tomcat8/logs/business_system/$${ctx:userId}/log.log` – Chetan Oswal Mar 11 '20 at 08:06
2

The Log4j2 FAQ page has an example that uses the RoutingAppender to achieve this.

Remko Popma
  • 35,130
  • 11
  • 92
  • 114