You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Log4j FileAppender间歇性FileNotFoundException问题求助

多线程下Log4j 1.1.3间歇性FileNotFoundException问题解决

问题根源

  1. getLogger()方法非线程安全:多线程并发调用时,多个线程会同时进入Logger初始化分支,重复执行文件创建与Appender添加操作,引发资源竞争。
  2. FileAppender父目录创建竞争:Log4j 1.1.3的setFile方法中,当日志文件父目录不存在时,多个线程会同时触发父目录创建逻辑。后执行的线程会因mkdirs()返回false(父目录已被前序线程创建),直接抛出原始的FileNotFoundException。

解决方案

1. 同步Logger初始化逻辑

给getLogger()的初始化部分添加线程安全控制,确保同一时间只有一个线程执行Logger创建与配置:

public Logger getLogger() {
    if (this.logger != null) {
        return this.logger;
    }
    // 双重检查锁避免不必要的同步
    synchronized (this.getClass()) {
        if (this.logger != null) {
            return this.logger;
        }
        String outputAttr = getAttributeValue(OUTPUT_FILE_ATTR_QNAME);
        if (outputAttr != null) {
            try {
                File file = new File(outputAttr);
                String outputFile = file.getCanonicalPath();
                // 提前创建父目录,规避FileAppender内部的竞争逻辑
                File parentDir = file.getParentFile();
                if (parentDir != null && !parentDir.exists()) {
                    parentDir.mkdirs();
                }
                this.logger = Logger.getLogger(outputFile);
                this.logger.setAdditivity(false);
                Layout layout = new PatternLayout(ProcessingNode.logPattern);
                FileAppender fileAppender = new FileAppender(layout, outputFile, false);
                fileAppender.setEncoding("UTF-8");
                this.logger.addAppender(fileAppender);
                this.logger.setLevel(Level.INFO);
                return this.logger;
            } catch (Exception e) {
                e.printStackTrace(System.err);
            }
        } else if (this.parent != null) {
            return this.parent.getLogger();
        }
    }
    return null;
}

2. 预创建日志父目录

在测试套件启动前,统一创建所有测试用例可能用到的日志文件父目录,避免运行时动态创建的竞争:

@BeforeSuite
public void initLogDirectories() {
    // 替换为实际获取所有测试日志路径的逻辑
    List<String> logFilePaths = getAllTestLogPaths();
    for (String path : logFilePaths) {
        File parentDir = new File(path).getParentFile();
        if (parentDir != null && !parentDir.exists()) {
            parentDir.mkdirs();
        }
    }
}

3. 升级Log4j版本(推荐)

Log4j 1.x已停止维护,存在大量线程安全与安全漏洞问题。升级至Log4j 2.x后,其内部实现了更完善的线程安全控制,文件创建逻辑的竞争问题已被修复。

4. 全局缓存Logger实例

用线程安全的缓存容器复用同一文件路径的Logger,避免重复创建:

private static final Map<String, Logger> LOGGER_CACHE = new ConcurrentHashMap<>();

public Logger getLogger() {
    if (this.logger != null) {
        return this.logger;
    }
    String outputAttr = getAttributeValue(OUTPUT_FILE_ATTR_QNAME);
    if (outputAttr != null) {
        try {
            File file = new File(outputAttr);
            String outputFile = file.getCanonicalPath();
            // 利用ConcurrentHashMap的原子方法确保单例初始化
            this.logger = LOGGER_CACHE.computeIfAbsent(outputFile, key -> {
                Logger logger = Logger.getLogger(key);
                logger.setAdditivity(false);
                Layout layout = new PatternLayout(ProcessingNode.logPattern);
                try {
                    File parentDir = file.getParentFile();
                    if (parentDir != null && !parentDir.exists()) {
                        parentDir.mkdirs();
                    }
                    FileAppender fileAppender = new FileAppender(layout, key, false);
                    fileAppender.setEncoding("UTF-8");
                    logger.addAppender(fileAppender);
                    logger.setLevel(Level.INFO);
                } catch (Exception e) {
                    e.printStackTrace(System.err);
                }
                return logger;
            });
            return this.logger;
        } catch (Exception e) {
            e.printStackTrace(System.err);
        }
    } else if (this.parent != null) {
        return this.parent.getLogger();
    }
    return null;
}

内容的提问来源于stack exchange,提问作者singh.indolia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 03:24:58