【问题标题】:Log4j FileAppender recreating deleted filesLog4j FileAppender 重新创建已删除的文件
【发布时间】:2012-08-19 19:58:44
【问题描述】:

我在我正在从事的项目中使用 Log4j 作为日志记录框架。我有以下情况: Log4j 被配置为将日志写入日志文件。在某些时候,此日志文件被复制到另一个目的地并被删除。日志框架继续工作,但日志没有写入日志文件,因为它被删除了。有没有办法告诉 Log4j 重新创建文件并继续将日志写入日志文件。

最好的问候, 拉希德

【问题讨论】:

标签: java log4j fileappender


【解决方案1】:

我浏览了log4j的源代码。初始化 FileAppender/RollingFileAppender 时,会创建一个指向该文件的 FileOutputStream 实例。创建一个新的FileDescriptor 对象来表示此文件连接。这就是原因,其他解决方案(例如通过 Cron 监视文件和通过覆盖在附加方法中创建文件)对我不起作用,因为新文件描述符已分配给新文件。 Log4j Writer 仍然指向旧的 FileDescriptor。

解决方案是检查文件是否存在,如果不存在,则调用 FileAppender 类中存在的 activeOptions 方法。

package org.apache.log4j;

import java.io.File;
import org.apache.log4j.spi.LoggingEvent;

public class ModifiedRollingFileAppender extends RollingFileAppender {

    @Override 
    public void append(LoggingEvent event) {
        checkLogFileExist();
        super.append(event);
    }

    private void checkLogFileExist(){
        File logFile = new File(super.fileName);
        if (!logFile.exists()) {
            this.activateOptions();
        }
    }
}

最后将这个添加到 log4j.properties 文件中:

log4j.rootLogger=DEBUG, A1
log4j.appender.A1=org.apache.log4j.ModifiedRollingFileAppender
log4j.appender.A1.File=/path/to/file
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss,SSS} %p %c{1}: %m%n

//Skip the below lines for FileAppender
log4j.appender.A1.MaxFileSize=10MB
log4j.appender.A1.MaxBackupIndex=2

注意:我已经针对 log4j 1.2.17 进行了测试

【讨论】:

    【解决方案2】:

    试试这个。我现在没有Linux机器,所以我不确定它是否可以解决性能问题。

    package wodong.test;
    import java.io.File;
    import java.io.IOException;
    
    import org.apache.log4j.FileAppender;
    import org.apache.log4j.spi.LoggingEvent;
    
    public class LastFileAppender extends FileAppender {
        @Override
        public void append(LoggingEvent event) {
    
            checkLogFileExist();
            super.append(event);
        }
    
        private void checkLogFileExist() {
            if (qw == null) {
                File logFile = new File(super.fileName);
                if (!logFile.exists()) {
                    try {
                        logFile.createNewFile();
                    } catch (IOException e) {
                        System.out.println("Error while create new log file.");
                    }
                }
            }
        }
    }
    

    【讨论】:

    • 你好。谢谢。实际上性能下降并不那么显着。我检查了 Apache Tomcat。那里的访问日志实现中使用了类似的代码。
    【解决方案3】:

    试试这个课程

    package wodong.test;
    import java.io.File;
    import java.io.IOException;
    
    import org.apache.log4j.FileAppender;
    import org.apache.log4j.spi.LoggingEvent;
    
    public class LastFileAppender extends FileAppender {
        @Override
        public void append(LoggingEvent event) {
            checkLogFileExist();
            super.append(event);
        }
        private void checkLogFileExist(){
            File logFile = new File(super.fileName);
            if (!logFile.exists()) {
                try {
                    logFile.createNewFile();
                } catch (IOException e) {
                    System.out.println("Error while create new log file.");
                }
            }
        }
    }
    

    同时编辑 log4j 配置文件

    log4j.appender.R=wodong.test.LastFileAppender
    

    【讨论】:

    • 我已经试过了。它可以工作,但性能会显着下降。使用这种 appender 进行日志记录大约慢 10 倍。
    【解决方案4】:

    我研究了log4j的来源,发现log4j无法创建新的日志文件,它只是在日志文件被删除时将错误消息打印到system.err

        /** 
         This method determines if there is a sense in attempting to append. 
    
         <p>It checks whether there is a set output target and also if 
         there is a set layout. If these checks fail, then the boolean 
         value <code>false</code> is returned. */  
    
      protected   boolean checkEntryConditions() {  
        if(this.closed) {  
          LogLog.warn("Not allowed to write to a closed appender.");  
          return false;  
        }  
    
        if(this.qw == null) {  
          errorHandler.error("No output stream or file set for the appender named ["+  
                name+"].");  
          return false;  
        }  
    
        if(this.layout == null) {  
          errorHandler.error("No layout set for the appender named ["+ name+"].");  
          return false;  
        }  
        return true;  
      }  
    

    我认为有两种解决方法

    1. 创建另一个 cron 线程来监控日志文件
    2. 在getLog或getInstance(singleton)中添加判断,检查日志文件是否存在,如果不存在则init log4j

    【讨论】:

    • 感谢您提供此代码片段。您建议的第一个解决方法不够简单。我想我会尝试实施您建议的第二种解决方法。你提到的getLog和getInstance这两个方法是在哪个类中的?
    【解决方案5】:

    确保您可以在 log4j 文件中声明这一行

        log4j.appender.rollingFile.File=D:/myapp/mylog.log
    

    如果您已经声明了它,您可以随意删除或替换日志文件。然后重新运行程序并在此路径中创建新的日志文件。

    【讨论】:

    • 感谢您的建议,但它对我不起作用。我不想重新启动应用程序。
    • 您不需要重新启动。您可以配置 RollingFileAppender 来创建新的日志文件。这意味着 log4j 将在它们“满”后立即重命名和移动它们,然后它将创建一个新的日志文件。
    • 我不明白这对我有什么作用。正如您所说,当现有文件已满时会创建新文件。就我而言,我需要在删除现有文件时创建新文件。
    猜你喜欢
    • 1970-01-01
    • 2012-04-13
    • 1970-01-01
    • 1970-01-01
    • 2021-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-25
    相关资源
    最近更新 更多