论坛首页 Java企业应用论坛

自定义log4j生成的log文件名

浏览 3849 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2011-11-01   最后修改:2011-11-01
    很多时候,log4j的RollingFileAppender配置如下:

log4j.logger.cn.lettoo.Test=INFO, file
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.MaxFileSize=100KB
log4j.appender.file.MaxBackupIndex=10
log4j.appender.file.File=test.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout  
log4j.appender.file.layout.ConversionPattern=[%d{MM/dd/yyyy HH:mm:ss.SSS} %m%n


    RollingFileAppender会自动在一个文件满了之后,生成一个如text.log.1的文件,一直生成到text.log.x(x值可配置log4j.appender.file.MaxBackupIndex=10)为止,再回写text.log。

    当我们有需要在产生一些自定义的log文件名的时候,比如:
引用

test_[process_name]_0_20111031.log
test_[process_name]_1_20111031.log
test_[process_name]_2_20111031.log
......

    要在文件名显示是哪一个进程名,第一个文件为0,后面是1,2,3......,并且要加上日期,再使用RollingFileAppender做简单的配置是无论如何也实现不了了。

    打开RollingFileAppender.java的源码,我看到它主要是用setFile(),subAppend()和rollOver()这几个方法来实现具体的功能的:
  public // synchronization not necessary since doAppend is alreasy synched
  void rollOver() {
    File target;
    File file;

    if (qw != null) {
        long size = ((CountingQuietWriter) qw).getCount();
        LogLog.debug("rolling over count=" + size);
        //   if operation fails, do not roll again until
        //      maxFileSize more bytes are written
        nextRollover = size + maxFileSize;
    }
    LogLog.debug("maxBackupIndex="+maxBackupIndex);

    boolean renameSucceeded = true;
    // If maxBackups <= 0, then there is no file renaming to be done.
    if(maxBackupIndex > 0) {
      // Delete the oldest file, to keep Windows happy.
      file = new File(fileName + '.' + maxBackupIndex);
      if (file.exists())
       renameSucceeded = file.delete();

      // Map {(maxBackupIndex - 1), ..., 2, 1} to {maxBackupIndex, ..., 3, 2}
      for (int i = maxBackupIndex - 1; i >= 1 && renameSucceeded; i--) {
	file = new File(fileName + "." + i);
	if (file.exists()) {
	  target = new File(fileName + '.' + (i + 1));
	  LogLog.debug("Renaming file " + file + " to " + target);
	  renameSucceeded = file.renameTo(target);
	}
      }

    if(renameSucceeded) {
      // Rename fileName to fileName.1
      target = new File(fileName + "." + 1);

      this.closeFile(); // keep windows happy.

      file = new File(fileName);
      LogLog.debug("Renaming file " + file + " to " + target);
      renameSucceeded = file.renameTo(target);
      //
      //   if file rename failed, reopen file with append = true
      //
      if (!renameSucceeded) {
          try {
            this.setFile(fileName, true, bufferedIO, bufferSize);
          }
          catch(IOException e) {
              if (e instanceof InterruptedIOException) {
                  Thread.currentThread().interrupt();
              }
              LogLog.error("setFile("+fileName+", true) call failed.", e);
          }
      }
    }
    }

    //
    //   if all renames were successful, then
    //
    if (renameSucceeded) {
    try {
      // This will also close the file. This is OK since multiple
      // close operations are safe.
      this.setFile(fileName, false, bufferedIO, bufferSize);
      nextRollover = 0;
    }
    catch(IOException e) {
        if (e instanceof InterruptedIOException) {
            Thread.currentThread().interrupt();
        }
        LogLog.error("setFile("+fileName+", false) call failed.", e);
    }
    }
  }

  public
  synchronized
  void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize)
                                                                 throws IOException {
    super.setFile(fileName, append, this.bufferedIO, this.bufferSize);
    if(append) {
      File f = new File(fileName);
      ((CountingQuietWriter) qw).setCount(f.length());
    }
  }


  /**
     This method differentiates RollingFileAppender from its super
     class.

     @since 0.9.0
  */
  protected
  void subAppend(LoggingEvent event) {
    super.subAppend(event);
    if(fileName != null && qw != null) {
        long size = ((CountingQuietWriter) qw).getCount();
        if (size >= maxFileSize && size >= nextRollover) {
            rollOver();
        }
    }
   }

    如果我继承这个类,然后在子类里实现它的这三个方法,不就可以实现我要的功能了吗?
代码如下:

public class MWLogFileAppender extends RollingFileAppender {

    private long nextRollover = 0;

    public void rollOver() {
        File target;
        File file;

        if (qw != null) {
            long size = ((CountingQuietWriter) qw).getCount();
            LogLog.debug("rolling over count=" + size);
            // if operation fails, do not roll again until
            // maxFileSize more bytes are written
            nextRollover = size + maxFileSize;
        }
        LogLog.debug("maxBackupIndex=" + maxBackupIndex);

        boolean renameSucceeded = true;
        // If maxBackups <= 0, then there is no file renaming to be done.
        if (maxBackupIndex > 0) {
            // Delete the oldest file, to keep Windows happy.
            //file = new File(fileName + '.' + maxBackupIndex);
            file = new File(getRollingFileName(fileName, maxBackupIndex));
            if (file.exists())
                renameSucceeded = file.delete();

            // Map {(maxBackupIndex - 1), ..., 2, 1} to {maxBackupIndex, ..., 3,
            // 2}
            for (int i = maxBackupIndex - 1; i >= 1 && renameSucceeded; i--) {
                //file = new File(fileName + "." + i);
                file = new File(getRollingFileName(fileName, i));
                if (file.exists()) {
                    //target = new File(fileName + '.' + (i + 1));
                    target = new File(getRollingFileName(fileName, i+1));
                    LogLog.debug("Renaming file " + file + " to " + target);
                    renameSucceeded = file.renameTo(target);
                }
            }

            if (renameSucceeded) {
                // Rename fileName to fileName.1
                //target = new File(fileName + "." + 1);
                target = new File(this.getRollingFileName(fileName, 1));

                this.closeFile(); // keep windows happy.

                file = new File(fileName);
                LogLog.debug("Renaming file " + file + " to " + target);
                renameSucceeded = file.renameTo(target);
                //
                // if file rename failed, reopen file with append = true
                //
                if (!renameSucceeded) {
                    try {
                        this.setFile(fileName, true, bufferedIO, bufferSize);
                    } catch (IOException e) {
                        if (e instanceof InterruptedIOException) {
                            Thread.currentThread().interrupt();
                        }
                        LogLog.error("setFile(" + fileName
                                + ", true) call failed.", e);
                    }
                }
            }
        }

        //
        // if all renames were successful, then
        //
        if (renameSucceeded) {
            try {
                // This will also close the file. This is OK since multiple
                // close operations are safe.
                this.setFile(fileName, false, bufferedIO, bufferSize);
                nextRollover = 0;
            } catch (IOException e) {
                if (e instanceof InterruptedIOException) {
                    Thread.currentThread().interrupt();
                }
                LogLog.error("setFile(" + fileName + ", false) call failed.", e);
            }
        }
    }

    private String getRollingFileName(String fileName, int index) {
        Pattern p = Pattern.compile("_\\d+\\."); 
        Matcher m=p.matcher(fileName);
        
        return m.replaceFirst(String.format("_%d.", index));
    }

    public synchronized void setFile(String fileName, boolean append,
            boolean bufferedIO, int bufferSize) throws IOException {
        String processName = "01";

        SimpleDateFormat format = new SimpleDateFormat("MMddyyyy");
        String dateString = format.format(new Date(System.currentTimeMillis()));

        String processId = String.valueOf(Thread.currentThread().getId());
        String temp = String.format(fileName, processName, dateString,
                processId);
        //System.out.println(temp);
        super.setFile(temp, append, bufferedIO, bufferSize);
    }

    protected void subAppend(LoggingEvent event) {
        super.subAppend(event);
        if (fileName != null && qw != null) {
            long size = ((CountingQuietWriter) qw).getCount();
            if (size >= maxFileSize && size >= nextRollover) {
                rollOver();
            }
        }
    }

}


    在setFile方法里,通过String的替换来实现我要的自定义字符串的功能,当然,这里只是一个示例。
    文件满了之后生成新的文件编号,不再在log后面加数据,而是在文件名体现,我这里加了一个getRollingFileName()来根据正则表达式来替换。

    当然,配置文件可以修改如下了:
log4j.logger.cn.lettoo.Test=INFO, file
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.MaxFileSize=100KB
log4j.appender.file.MaxBackupIndex=10
log4j.appender.file.File=test-%s_%s_0.%s.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout  
log4j.appender.file.layout.ConversionPattern=[%d{MM/dd/yyyy HH:mm:ss.SSS} %m%n


    虽然以上的做法满足了我的要求,但是我感觉也有缺点,主要是:
  • 配置文件必须要按我自定义的格式来写,即log4j.appender.file.File=test-%s_%s_0.%s.log不能搞错了
  • RollingFileAppender的几个方法扩展的都不好,还把原来的代码copy过来修改的。


    目前也没有想到更好的方法,暂时先记下来。
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics