如何在运行时为每个 class 再次初始化记录器 log4j

How to initialize logger log4j again for each class during runtime

我以前从未使用过日志记录,我想在应用程序中间再次为每个 class 初始化记录器。我们正在使用 log4jeach class,我们有

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

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

所以我想要的是在我的应用程序运行时的某个时间点重新初始化这个 logger 对象。到目前为止,我注意到这个 logger 对象是在 application startup 上初始化的,但我想执行这个

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

行,而我的应用程序是 运行 并且用户执行特定操作。

Objective:

我真正的objective是将这些日志文件存储在磁盘上。但我想根据用户操作将日志文件存储在不同的文件夹中。所以我们的应用程序中有两个 siteIDs,因此,我们在磁盘上创建了 2 个文件夹,我们希望在其中存储日志文件。 So, when the site is selected these logger objects on each class needs to be reinitialized.有什么建议我怎样才能做到这一点?以下是我的 log.properties 文件:

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

然后我们有一个 class 名为 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
 */ 

最后是另一个记录器文件:

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;
    }
    
}

请注意 orgID 是用户更改的站点 ID,在更改它时,我想重新初始化所有 logger 个对象

Log4j2 中解决问题的标准方法是使用单个记录器 ThreadContext and a RoutingAppender。这几乎不需要编程配置:

  1. 在请求处理开始时,您需要为 siteId ThreadContext 键填写一个值:
    ThreadContext.put("siteId", "site1");
    
  2. 在您的 log4j2.xml 文件中,您需要配置一个路由附加程序,它根据 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. 您的 类 可以使用从 LogManager.getLogger 获得的静态记录器,不需要了解附加程序选择背后的逻辑。

在Logback中使用过滤器大概可以得到类似的配置,但我对Logback的了解还很有限。