Skip to content

create a custom log4j2 rolling file appender

I want to create a custom log4j2 rolling file appender. I need to create this custom appender because I want to wrap the file name with current thread name. We are trying to migrate log4j 1.x to recent log4j2 version and previously we had used DailyRollingFileAppender to log all activities of our application.

please find the below code.

Here we are trying to append the log to a file on daily basis with help of DailyRollingFileAppender based on threadName.

Since DailyRollingFileAppender is deprecated in recent version -so, how to create custom rolling file appender with incorporating our thread based logic.?

Find the below log4j.properties file

log4j.logger.***=INFO, FileLogger
# log4j.appender.FileLogger=org.apache.log4j.DailyRollingFileAppender
# Custom Appendar which will redirect the logs based on thread names configured using 
# log4j.appender.FileLogger.threadNameMapping property below
log4j.appender.FileLogger=********.framework.log4j.appender.ThreadNamePatternAppender 
log4j.appender.FileLogger.DatePattern='.'yyyy-MM-dd
log4j.appender.FileLogger.file=/logs/fileName.log
log4j.appender.FileLogger.layout=org.apache.log4j.PatternLayout
log4j.appender.FileLogger.layout.ConversionPattern=%d [%-5p] [%t] [%c{1}] [%M] - %m%n
# Custom property to hold mapping between thread names and log file for plug-in
# Beware - ThreadNamePatternAppender class inherits DailyRollingFileAppender hence it will not work for any other type of appender
# This can be distuingished using - ThreadName1>ThreadName1.log|ThreadName2>ThreadName2.log|.....|ThreadNameN>ThreadNameN.log
# Note - If there is no mapping for a particular thread then logs will be written to default log file
log4j.appender.FileLogger.threadNameMapping=********/logs/fileName-fileName.log

Thanks!

import java.util.HashMap;
import java.util.Map;

import org.apache.logging.log4j.core.appender.RollingFileAppender;

import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.spi.LoggingEvent;


public class ThreadNamePatternAppender extends DailyRollingFileAppender {
    private Map<String, DailyRollingFileAppender> threadBasedSubAppenders = new HashMap<String, DailyRollingFileAppender>();
    private String threadNameMapping;

    public String getThreadNameMapping() {
        return threadNameMapping;
    }

    public void setThreadNameMapping(String threadNameMapping) {
        this.threadNameMapping = threadNameMapping;
    }

    @Override
    public void activateOptions() {
        super.activateOptions();
        if (threadNameMapping != null && threadNameMapping.trim().length() > 0) {
            DailyRollingFileAppender tempAppender;
            String[] threadNames = threadNameMapping.split("\|");
            for (String threadName : threadNames) {
                if (threadName != null && threadName.length() > 0) {
                    try {
                        LogLog.debug(String.format("Creating new appender for thread %s", threadName));
                        tempAppender = new DailyRollingFileAppender(getLayout(), threadName.split(">")[1],
                                getDatePattern());
                        threadBasedSubAppenders.put(threadName.split(">")[0], tempAppender);
                    } catch (Exception ex) {
                        LogLog.error("Failed to create appender", ex);
                    }
                }
            }
        }
    }

    @Override
    public void append(LoggingEvent event) {
        String threadName = event.getThreadName().split(" ")[0];
        if (threadBasedSubAppenders.containsKey(threadName)) {
            threadBasedSubAppenders.get(threadName).append(event);
        } else {
            super.append(event);
        }
    }

    @Override
    public synchronized void close() {
        LogLog.debug("Calling Close on ThreadNamePatternAppender" + getName());
        for (DailyRollingFileAppender appender : threadBasedSubAppenders.values()) {
            appender.close();
        }
        this.closed = true;
    }
}



  

Answer

The RollingFileAppender in Log4j 2.x is final, so you can not extend it. However you can obtain the functionality of your custom Log4j 1.x appender using:

For a simple logfile-per-thread appender you can use:

<Routing name="Routing">
  <Routes pattern="$${event:ThreadName}">
    <Route>
      <RollingFile name="Rolling-${event:ThreadName}"
                   fileName="logs/thread-${event:ThreadName}.log"
                   filePattern="logs/thread-${event:ThreadName}.log.%d{yyyy-MM-dd}">
        <PatternLayout pattern="%d [%-5p] [%t] [%c{1}] [%M] - %m%n" />
        <TimeBasedTriggeringPolicy />
      </RollingFile>
    </Route>
  </Routes>
</Routing>

For a more complex configuration both the <Routing> appender and the <Routes> can contain a <Script> (cf. documentation):

  • the script in the <Routing> appender can initialize the staticVariables map and return a default route,
  • the script in the <Routes> component chooses the appropriate route based on staticVariables and the logging event.