DailyRollingFileAppender-重写fileAppender设置文件大小和备份数

DailyRollingFileAppender-重写fileAppender设置文件大小和备份数

import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;

import org.apache.log4j.FileAppender;
import org.apache.log4j.Layout;
import org.apache.log4j.helpers.CountingQuietWriter;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.helpers.OptionConverter;
import org.apache.log4j.spi.LoggingEvent;

public class MyDailyRollingFileAppender extends FileAppender{

 // The code assumes that the following constants are in a increasing  
// sequence.  
static final int TOP_OF_TROUBLE = -1;  
static final int TOP_OF_MINUTE = 0;  
static final int TOP_OF_HOUR = 1;  
static final int HALF_DAY = 2;  
static final int TOP_OF_DAY = 3;  
static final int TOP_OF_WEEK = 4;  
static final int TOP_OF_MONTH = 5;  

/** 
 * The default maximum file size is 10MB. 
 */  
protected long maxFileSize = 10 * 1024 * 1024;  

/** 
 * There is one backup file by default. 
 */  
protected int maxBackupIndex = 1;  

/** 
 * The date pattern. By default, the pattern is set to "'.'yyyy-MM-dd" 
 * meaning daily rollover. 
 */  
private String datePattern = "'.'yyyy-MM-dd";  

/** 
 * The log file will be renamed to the value of the scheduledFilename 
 * variable when the next interval is entered. For example, if the rollover 
 * period is one hour, the log file will be renamed to the value of 
 * "scheduledFilename" at the beginning of the next hour. 
 *  
 * The precise time when a rollover occurs depends on logging activity. 
 */  
private String scheduledFilename;  

/** 
 * The next time we estimate a rollover should occur. 
 */  
private long nextCheck = System.currentTimeMillis() - 1;  

Date now = new Date();  

SimpleDateFormat sdf;  

RollingCalendar rc = new RollingCalendar();  

int checkPeriod = TOP_OF_TROUBLE;  

// The gmtTimeZone is used only in computeCheckPeriod() method.  
static final TimeZone gmtTimeZone = TimeZone.getTimeZone("GMT");  

/** 
 * The default constructor does nothing. 
 */  
public MyDailyRollingFileAppender() {  
}  

/** 
 * Instantiate a <code>MyDailyRollingFileAppender</code> and open the file 
 * designated by <code>filename</code>. The opened filename will become the 
 * ouput destination for this appender. 
 */  
public MyDailyRollingFileAppender(Layout layout, String filename,  
        String datePattern) throws IOException {  
    super(layout, filename, true);  
    this.datePattern = datePattern;  
    activateOptions();  
}  

/** 
 * Get the maximum size that the output file is allowed to reach before 
 * being rolled over to backup files. 
 *  
 * @since 1.1 
 */  
public long getMaximumFileSize() {  
    return maxFileSize;  
}  

/** 
 * Set the maximum size that the output file is allowed to reach before 
 * being rolled over to backup files. 
 *  
 * <p> 
 * This method is equivalent to {@link #setMaxFileSize} except that it is 
 * required for differentiating the setter taking a <code>long</code> 
 * argument from the setter taking a <code>String</code> argument by the 
 * JavaBeans {@link java.beans.Introspector Introspector}. 
 *  
 * @see #setMaxFileSize(String) 
 */  
public void setMaximumFileSize(long maxFileSize) {  
    this.maxFileSize = maxFileSize;  
}  

/** 
 * Set the maximum size that the output file is allowed to reach before 
 * being rolled over to backup files. 
 *  
 * <p> 
 * In configuration files, the <b>MaxFileSize</b> option takes an long 
 * integer in the range 0 - 2^63. You can specify the value with the 
 * suffixes "KB", "MB" or "GB" so that the integer is interpreted being 
 * expressed respectively in kilobytes, megabytes or gigabytes. For example, 
 * the value "10KB" will be interpreted as 10240. 
 */  
public void setMaxFileSize(String value) {  
    maxFileSize = OptionConverter.toFileSize(value, maxFileSize + 1);  
}  

/** 
 * Returns the value of the <b>MaxBackupIndex</b> option. 
 */  
public int getMaxBackupIndex() {  
    return maxBackupIndex;  
}  

/** 
 * Set the maximum number of backup files to keep around. 
 *  
 * <p> 
 * The <b>MaxBackupIndex</b> option determines how many backup files are 
 * kept before the oldest is erased. This option takes a positive integer 
 * value. If set to zero, then there will be no backup files and the log 
 * file will be truncated when it reaches <code>MaxFileSize</code>. 
 */  
public void setMaxBackupIndex(int maxBackups) {  
    this.maxBackupIndex = maxBackups;  
}  

/** 
 * The <b>DatePattern</b> takes a string in the same format as expected by 
 * {@link SimpleDateFormat}. This options determines the rollover schedule. 
 */  
public void setDatePattern(String pattern) {  
    datePattern = pattern;  
}  

/** Returns the value of the <b>DatePattern</b> option. */  
public String getDatePattern() {  
    return datePattern;  
}  

public void activateOptions() {  
    super.activateOptions();  
    if (datePattern != null && fileName != null) {  
        now.setTime(System.currentTimeMillis());  
        sdf = new SimpleDateFormat(datePattern);  
        int type = computeCheckPeriod();  
        printPeriodicity(type);  
        rc.setType(type);  
        File file = new File(fileName);  
        scheduledFilename = fileName  
                + sdf.format(new Date(file.lastModified()));  

    } else {  
        LogLog.error("Either File or DatePattern options are not set for appender ["  
                + name + "].");  
    }  
}  

void printPeriodicity(int type) {  
    switch (type) {  
    case TOP_OF_MINUTE:  
        LogLog.debug("Appender [" + name + "] to be rolled every minute.");  
        break;  
    case TOP_OF_HOUR:  
        LogLog.debug("Appender [" + name  
                + "] to be rolled on top of every hour.");  
        break;  
    case HALF_DAY:  
        LogLog.debug("Appender [" + name  
                + "] to be rolled at midday and midnight.");  
        break;  
    case TOP_OF_DAY:  
        LogLog.debug("Appender [" + name + "] to be rolled at midnight.");  
        break;  
    case TOP_OF_WEEK:  
        LogLog.debug("Appender [" + name  
                + "] to be rolled at start of week.");  
        break;  
    case TOP_OF_MONTH:  
        LogLog.debug("Appender [" + name  
                + "] to be rolled at start of every month.");  
        break;  
    default:  
        LogLog.warn("Unknown periodicity for appender [" + name + "].");  
    }  
}  

// This method computes the roll over period by looping over the  
// periods, starting with the shortest, and stopping when the r0 is  
// different from from r1, where r0 is the epoch formatted according  
// the datePattern (supplied by the user) and r1 is the  
// epoch+nextMillis(i) formatted according to datePattern. All date  
// formatting is done in GMT and not local format because the test  
// logic is based on comparisons relative to 1970-01-01 00:00:00  
// GMT (the epoch).  

int computeCheckPeriod() {  
    RollingCalendar rollingCalendar = new RollingCalendar(gmtTimeZone,  
            Locale.ENGLISH);  
    // set sate to 1970-01-01 00:00:00 GMT  
    Date epoch = new Date(0);  
    if (datePattern != null) {  
        for (int i = TOP_OF_MINUTE; i <= TOP_OF_MONTH; i++) {  
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat(  
                    datePattern);  
            simpleDateFormat.setTimeZone(gmtTimeZone); // do all date  
                                                        // formatting in GMT  
            String r0 = simpleDateFormat.format(epoch);  
            rollingCalendar.setType(i);  
            Date next = new Date(rollingCalendar.getNextCheckMillis(epoch));  
            String r1 = simpleDateFormat.format(next);  
            // System.out.println("Type = "+i+", r0 = "+r0+", r1 = "+r1);  
            if (r0 != null && r1 != null && !r0.equals(r1)) {  
                return i;  
            }  
        }  
    }  
    return TOP_OF_TROUBLE; // Deliberately head for trouble...  
}  

/** 
 * Implements the usual roll over behaviour. 
 *  
 * <p> 
 * If <code>MaxBackupIndex</code> is positive, then files { 
 * <code>File.1</code>, ..., <code>File.MaxBackupIndex -1</code> are renamed 
 * to {<code>File.2</code>, ..., <code>File.MaxBackupIndex</code> . 
 * Moreover, <code>File</code> is renamed <code>File.1</code> and closed. A 
 * new <code>File</code> is created to receive further log output. 
 *  
 * <p> 
 * If <code>MaxBackupIndex</code> is equal to zero, then the 
 * <code>File</code> is truncated with no backup files created. 
 */  
public// synchronization not necessary since doAppend is alreasy synched  
void sizeRollOver() {  
    File target;  
    File file;  

    LogLog.debug("rolling over count="  
            + ((CountingQuietWriter) qw).getCount());  
    LogLog.debug("maxBackupIndex=" + maxBackupIndex);  

    String datedFilename = fileName + sdf.format(now);  

    if (maxBackupIndex > 0) {  
        // Delete the oldest file, to keep Windows happy.  
        file = new File(datedFilename + '.' + maxBackupIndex);  
        if (file.exists())  
            file.delete();  

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

        // Rename fileName to datedFilename.1  
        target = new File(datedFilename + "." + 1);  

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

        file = new File(fileName);  
        LogLog.debug("Renaming file " + file + " to " + target);  
        file.renameTo(target);  
    }else if (maxBackupIndex < 0){//infinite number of files   
        //find the max backup index  
        for (int i = 1; i < Integer.MAX_VALUE; i++) {  
            target = new File(datedFilename + "." + i);  
            if (! target.exists()) {//Rename fileName to datedFilename.i  
                this.closeFile();  
                file = new File(fileName);  
                file.renameTo(target);  
                LogLog.debug("Renaming file " + file + " to " + target);  
                break;  
            }  
        }  
    }  

    try {  
        // This will also close the file. This is OK since multiple  
        // close operations are safe.  
        this.setFile(fileName, false, bufferedIO, bufferSize);  
    } catch (IOException e) {  
        LogLog.error("setFile(" + fileName + ", false) call failed.", e);  
    }  
    scheduledFilename = datedFilename;  
}  

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

protected void setQWForFiles(Writer writer) {  
    this.qw = new CountingQuietWriter(writer, errorHandler);  
}  

/** 
 * Rollover the current file to a new file. 
 */  
void timeRollOver() throws IOException {  

    /* Compute filename, but only if datePattern is specified */  
    if (datePattern == null) {  
        errorHandler.error("Missing DatePattern option in rollOver().");  
        return;  
    }  

    String datedFilename = fileName + sdf.format(now);  
    // It is too early to roll over because we are still within the  
    // bounds of the current interval. Rollover will occur once the  
    // next interval is reached.  
    if (scheduledFilename.equals(datedFilename)) {  
        return;  
    }  

    // close current file, and rename it to datedFilename  
    this.closeFile();  

    File target = new File(scheduledFilename);  
    if (target.exists()) {  
        target.delete();  
    }  

    File file = new File(fileName);  
    boolean result = file.renameTo(target);  
    if (result) {  
        LogLog.debug(fileName + " -> " + scheduledFilename);  
    } else {  
        LogLog.error("Failed to rename [" + fileName + "] to ["  
                + scheduledFilename + "].");  
    }  

    try {  
        // This will also close the file. This is OK since multiple  
        // close operations are safe.  
        super.setFile(fileName, false, this.bufferedIO, this.bufferSize);  
    } catch (IOException e) {  
        errorHandler.error("setFile(" + fileName + ", false) call failed.");  
    }  
    scheduledFilename = datedFilename;  
}  

/** 
 * This method differentiates MyDailyRollingFileAppender from its super class. 
 *  
 * <p> 
 * Before actually logging, this method will check whether it is time to do 
 * a rollover. If it is, it will schedule the next rollover time and then 
 * rollover. 
 * */  
protected void subAppend(LoggingEvent event) {  
    long n = System.currentTimeMillis();  

    if (n >= nextCheck) {  
        now.setTime(n);  
        nextCheck = rc.getNextCheckMillis(now);  
        try {  
            timeRollOver();  
        } catch (IOException ioe) {  
            LogLog.error("rollOver() failed.", ioe);  
        }  
    } else if ((fileName != null)  
            && ((CountingQuietWriter) qw).getCount() >= maxFileSize) {  
        sizeRollOver();  
    }  
    super.subAppend(event);  

}  

}

2.配置文件
#monitor
log4j.logger.monitorlog=INFO,monitorFile
log4j.additivity.monitorlog = false
log4j.appender.monitorFile=com.xxx.xxx.util.MyDailyRollingFileAppender
log4j.appender.monitorFile.File=/route/lala.log #日志路径
log4j.appender.monitorFile.MaxFileSize=2KB #文件大小
log4j.appender.monitorFile.maxBackupIndex=2 #文件个数限制
log4j.appender.monitorFile.layout=org.apache.log4j.PatternLayout
log4j.appender.monitorFile.layout.ConversionPattern=%m%n

3.为何重写这个类呢 ?
Log4j现在已经被大家熟知了,所有细节都可以在网上查到,Log4j支持Appender,其中DailyRollingFileAppender是被经常用到的Appender之一。在讨论今天的主题之前,我们先看下另外一个Appender。

最常用的Appender——RollingFileAppender
下面是RollingFileAppender的一个Log4j配置样例(配置1):

log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.Threshold=DEBUG
log4j.appender.R.File=test.log
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%5p] - %c -%F(%L) -%m%n
log4j.appender.R.MaxFileSize=20MB
log4j.appender.R.MaxBackupIndex=10

RollingFileAppender使用MaxFileSize设置一个日志文件的最大大小,当产生多个日志时,会在日志名称后面加上".1"、".2"、……这样的后缀,我们可以看到RollingFileAppender有个属性MaxBackupIndex,这个属性通过限制日志文件名后缀".n"中的n大小来限制日志数量,比如上面MaxBackupIndex=10,其实最大日志数量为11。我们知道这个有这个限制是很必要的,当我们的程序在服务器上运行时,随着时间的迁移,日志会越来越多,如果对日志数量没有限制,日志大小会越来越大,最后甚至占满整个硬盘。

可以按照周期时间来滚动日志文件的Appender——DailyRollingFileAppender
下面是DailyRollingFileAppender的一个Log4j配置样例(配置2):

log4j.appender.logfile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.logfile.File=test.log
log4j.appender.logfile.DataPattern=’.'yyyy-MM-dd-HH-mm
log4j.appender.logfile.Threshold=debug
log4j.appender.logfile.encoding=UTF-8
log4j.appender.logfile.Append=false
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern= [%d{yyyy-MM-dd HH:mm:ss}]%-5p %c(line:%L) %x-%m%n

DailyRollingFileAppender特点是固定周期时间生成一个日志文件,比如,默认情况是每天生成一个文件。这种日志可以方便根据时间来定位日志位置,使日志清晰易查。但是这种日志有个不好地方是,不能限制日志数量,MaxBackupIndex属性和MaxFileSize在DailyRollingFileAppender中是无效的,我们上面已经提到限制日志数量的必要性。这里有两个解决办法:

linux上crontab+shell
java进程里面起一个线程,定期扫描日志文件夹。
但是这两种方法都不是很方便,有没有更好的办法呢?

重写DailyRollingFileAppender——MyDailyRollingFileAppender
查看DailyRollingFileAppender源代码,发现rollOver()方法是用来生成文件的,当调用subAppend()方法时会根据判断当前时间是否大于应该生成新文件的时间了(具体实现可以查看源码,逻辑还是比较清晰的),如果大于,就生成。首先把当前日志重命名,命名格式为test.log.yyyy-MM-dd-HH-mm,然后重新建test.log文件。看到这里我们就可以想,在rollOver()方法里面加上删除过多的日志

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值