Search code examples
javalogginglog4j

How to initialize logger log4j again for each class during runtime


I have never worked with logging before and I want to initialize the logger for each class again in the middle of the application. We are using log4j and On each class, we have

public class MyClass {
    
    private static Logger logger = Logger.getLogger(MyClass.class);

    public void method(){
        logger.info("This is info");
    }

So what I want is to reinitialize this logger object at a certain point at runtime in my application. So far I have noticed that this logger object is initialized on application startup but I want to execute this

private static Logger logger = Logger.getLogger(MyClass.class);

line while my application is running and user performs a certain action.

Objective:

My real objective is to store these log files on disk. But I want to store the log files on different folders depending on user actions. So we have two siteIDs in our application and therefore, we have made 2 folders on disk where we expect log files to be stored. So, when the site is selected these logger objects on each class needs to be reinitialized. Any suggestions how can I achieve this? Following is my log.properties file:

logger.pattern=%d{dd-MM-yyyy HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%M - %msg%n
logger.base.dir=../logs
logger.archive.dir=/archive
logger.archived.file.pattern=_%d{dd-MM-yyyy}.%i
logger.file.ext=.log
logger.file.size=10 mb
logger.archived.size.cap=100 mb
logger.file.retention.days=7
logger.debug.file.retention.days=1
logger.level=TRACE

Then we have a class named LoggerConfig

public class LoggerConfig {

private static LoggerConfig loggerConfig = null;

private Properties prop = new Properties();

private String pattern;
private String baseDir;
private String archiveDir;
private String archivedFilePattern;
private String fileExt;
private String fileSize;
private String archivedSizeCap;
private Integer fileRetentionDays;
private Integer debugFileRetentionDays;
private Level loggerLevel;

private LoggerConfig() {

    loadProperties();
}

public static LoggerConfig getInstance() {
    if (loggerConfig == null) {
        loggerConfig = new LoggerConfig();
    }
    return loggerConfig;
}

private void loadProperties() {
    try {
        InputStream input = LoggerConfig.class.getClassLoader().getResourceAsStream("logger.properties");
        prop.load(input);
        this.pattern = prop.getProperty("logger.pattern");
        this.baseDir = prop.getProperty("logger.base.dir");
        this.archiveDir = prop.getProperty("logger.archive.dir");
        this.archivedFilePattern = prop.getProperty("logger.archived.file.pattern");
        this.fileExt = prop.getProperty("logger.file.ext");
        this.fileSize = prop.getProperty("logger.file.size");
        this.archivedSizeCap = prop.getProperty("logger.archived.size.cap");
        this.fileRetentionDays = Integer.parseInt(prop.getProperty("logger.file.retention.days"));
        this.debugFileRetentionDays = Integer.parseInt(prop.getProperty("logger.debug.file.retention.days"));
        this.loggerLevel = Level.toLevel(prop.getProperty("logger.level"));
    } catch (Exception ex) {
        System.out.println("Exception occourred while loading the configration file " + ex);
    }
}

/**
 * Getters and setters
 */ 

And Finally another Logger file:

public class ApplicationLogger {

    private static LoggerConfig config = LoggerConfig.getInstance();

    private static Map<String, Logger> loggers = new HashMap<String, Logger>();

    public static Logger getLogger(String orgID, Class<?> className) {
        init(orgID, className);
        return loggers.get(orgID);
    }

    private static void init(String orgID, Class<?> className) {
        if (!loggers.containsKey(orgID)) {
            LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
            Logger logger = loggerContext.getLogger(orgID + className);
            logger.setAdditive(Boolean.FALSE);
            logger.setLevel(config.getLoggerLevel());

            logger.addAppender(getFileAppender(loggerContext, Level.TRACE, orgID));
            logger.addAppender(getFileAppender(loggerContext, Level.DEBUG, orgID));
            logger.addAppender(getFileAppender(loggerContext, Level.INFO, orgID));
            logger.addAppender(getFileAppender(loggerContext, Level.WARN, orgID));
            logger.addAppender(getFileAppender(loggerContext, Level.ERROR, orgID));
            
            loggers.put(orgID, logger);
        }
    }

    private static FileAppender<ILoggingEvent> getFileAppender(LoggerContext loggerContext, Level level, String orgID) {

        PatternLayoutEncoder encoder = new PatternLayoutEncoder();
        encoder.setContext(loggerContext);
        encoder.setPattern(config.getPattern());
        encoder.setImmediateFlush(Boolean.TRUE);
        encoder.start();

        LevelFilter filter = new LevelFilter();
        filter.setContext(loggerContext);
        filter.setLevel(level);
        filter.setOnMismatch(FilterReply.DENY);
        filter.start();

        RollingFileAppender<ILoggingEvent> fileAppender = new RollingFileAppender<ILoggingEvent>();

        SizeAndTimeBasedRollingPolicy<ILoggingEvent> rollingPolicy = new SizeAndTimeBasedRollingPolicy<ILoggingEvent>();
        rollingPolicy.setContext(loggerContext);
        rollingPolicy.setFileNamePattern(config.getBaseDir() + config.getArchiveDir() + File.separator + orgID
                + File.separator + level.toString() + config.getArchivedFilePattern() + config.getFileExt());
        rollingPolicy.setMaxFileSize(FileSize.valueOf(config.getFileSize()));
        rollingPolicy.setMaxHistory(
                Level.DEBUG.equals(level) ? config.getDebugFileRetentionDays() : config.getFileRetentionDays());
        rollingPolicy.setTotalSizeCap(FileSize.valueOf(config.getArchivedSizeCap()));
        rollingPolicy.setParent(fileAppender);
        rollingPolicy.start();
        fileAppender.setContext(loggerContext);
        fileAppender.setAppend(true);
        fileAppender.setEncoder(encoder);
        fileAppender.addFilter(filter);
        fileAppender.setFile(
                config.getBaseDir() + File.separator + orgID + File.separator + level.toString() + config.getFileExt());
        fileAppender.setRollingPolicy(rollingPolicy);
        fileAppender.start();

        return fileAppender;
    }
    
}

Please note orgID is the siteID which is changed by user and on changing it, I want to reinitialize all logger objects


Solution

  • A standard solution to you problem in Log4j2 is to use a single logger, the ThreadContext and a RoutingAppender. This requires almost no programmatic configuration:

    1. At the beginning of your request processing your need to fill in a value for the siteId ThreadContext key:
      ThreadContext.put("siteId", "site1");
      
    2. In your log4j2.xml file you need to configure a routing appender that chooses the real appender based on the value of siteId:
      <Configuration>
        <Appenders>
          <RollingFile name="ROLLING-site1" fileName="..." filePattern="...">
            ...
          </RollingFile>
          <RollingFile name="ROLLING-site2" fileName="..." filePattern="...">
            ...
          </RollingFile>
          <Routing name="ROUTING">
            <Routes pattern="$${ctx:siteId}">
              <Route key="site1" appenderRef="ROLLING-site1" />
              <Route key="site2" appenderRef="ROLLING-site2" />
            </Routes>
          </Routing>
        </Appenders>
        <Loggers>
          <Root level="DEBUG">
            <AppenderRef ref="ROUTING" />
          </Root>
        </Loggers>
      </Configuration>
      
    3. Your classes can use a static logger obtained from LogManager.getLogger and do not need to be aware of the logic behind the choice of the appender.

    A similar configuration can probably be obtained in Logback using filters, but my knowledge of Logback is quite limited.