删除N天前的log4j日志

5 篇文章 0 订阅

测试工程下载地址:http://download.csdn.net/detail/mr__fang/6768099

本文采用第二种方案

方案原地址:http://www.codeproject.com/Articles/81462/DailyRollingFileAppender-with-maxBackupIndex


最近要实现定期删除N天前的日志。 以前都是利用运维的一个cron脚本来定期删除的, 总觉得可移植性不是很好, 比如要指定具体的日志文件路径, 有时候想想为什么log4j自己不实现这个功能呢? 后来发现在logback中已经实现了这个功能. 其配置如下: 

     <appender name="vstore"
          class="ch.qos.logback.core.rolling.RollingFileAppender">
          <file>default.log</file>
          <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
               <fileNamePattern>./logs/%d/default.log.%d
               </fileNamePattern>
               <maxHistory>7</maxHistory>
          </rollingPolicy>

          <encoder>
               <pattern>%d{HH:mm:ss.SSS} %level [%thread] %c{0}[%line] %msg%n
               </pattern>
          </encoder>
     </appender>

但是我的应用因为依赖的log相关的jar包的问题, 没法使用logback的jar包, 因为必须使用新的方式来处理. 于是google了一下, 发现老外也遇到了类似的问题. 比如这里是一个解决办法: 
http://stackoverflow.com/questions/1050256/how-can-i-get-log4j-to-delete-old-rotating-log-files  
这个方式是采用的RollingFileAppender, 然后通过设置maxBackupIndex属性来指定要保留的日志的最大值(这里采用一天一个日志).但是我们一直采用的是DailyRollingFileAppender. 可是该appender没有maxBackupIndex这个属性, 于是又google了一把, 发现老外也想到的解决办法, 而且这里有两个解决方法, 即在RollingFileAppender的基础上, 加入了maxBackupIndex属性. 

一种实现方案: 
http://www.zybonics.com/zybocodes/code/CodeViewForm.php?codeID=428 

/**************************
* Zybocodes *******************************
* Code For :
* log4j custom DailyRollingFileAppender - manage your
* logs:maxBackupIndex zip roll archive logging log management
* logs
* Contributor : Ed Sarrazin
* Ref link : http://jconvert.sourceforge.net
* For this and other codes/logic under any technology visit:
* http://www.zybonics.com/zybocodes/
********************************************************************/

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package logger;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.text.ParseException;
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 java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

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

/**
* @author Ed Sarrazin
*  <pre>
*  AdvancedDailyRollingFileAppender is a copy of apaches DailyRollingFileAppender.  This copy was made because it could not be extended due to package level access.  This new version will allow two new properties to be set:
*  MaxNumberOfDays:            Max number of log files to keep, denoted in days.  If using compression, this days should be longer than
*  -                           CompressBackupsAfterDays and will become irrelevant as files will be moved to archive before this time.
*  CompressBackups:            Indicating if older log files should be backed up to a compressed format.
*  RollCompressedBackups:      TURE/FALSE indicating that compressed backups should be rolled out (deleted after certain age)
*  CompressBackupsAfterDays:   Number of days to wait until adding files to compressed backup. (Files that are compressed are deleted)
*  CompressBackupsDatePattern: example - "'.'yyyy-MM" - this will create compressed backups grouped by pattern.  In this example, every month
*  CompressMaxNumberDays:      Number of days to keep compressed backups.  RollCompressedBackups must be true.
* 
*  Here is a listing of the log4j.properties file you would use:
* 
*  log4j.appender.RootAppender=com.edsdev.log4j.AdvancedDailyRollingFileAppender
*  log4j.appender.RootAppender.DatePattern='.'yyyyMMdd
*  log4j.appender.RootAppender.MaxNumberOfDays=60
*  log4j.appender.RootAppender.CompressBackups=true
*  log4j.appender.RootAppender.CompressBackupsAfterDays=31
*  log4j.appender.RootAppender.CompressBackupsDatePattern='.'yyyyMM
*  log4j.appender.RootAppender.RollCompressedBackups=true
*  log4j.appender.RootAppender.CompressMaxNumberDays=365
* 
*  </pre>
* AdvancedDailyRollingFileAppender extends {@link FileAppender} so that the underlying file is rolled over at a user
* chosen frequency. AdvancedDailyRollingFileAppender has been observed to exhibit synchronization issues and data loss.
* The log4j extras companion includes alternatives which should be considered for new deployments and which are
* discussed in the documentation for org.apache.log4j.rolling.RollingFileAppender.
* <p>
* The rolling schedule is specified by the <b>DatePattern</b> option. This pattern should follow the
* {@link SimpleDateFormat} conventions. In particular, you <em>must</em> escape literal text within a pair of single
* quotes. A formatted version of the date pattern is used as the suffix for the rolled file name.
* <p>
* For example, if the <b>File</b> option is set to <code>/foo/bar.log</code> and the <b>DatePattern</b> set to
* <code>'.'yyyy-MM-dd</code>, on 2001-02-16 at midnight, the logging file <code>/foo/bar.log</code> will be copied
* to <code>/foo/bar.log.2001-02-16</code> and logging for 2001-02-17 will continue in <code>/foo/bar.log</code>
* until it rolls over the next day.
* <p>
* Is is possible to specify monthly, weekly, half-daily, daily, hourly, or minutely rollover schedules.
* <p>
* <table border="1" cellpadding="2">
* <tr>
* <th>DatePattern</th>
* <th>Rollover schedule</th>
* <th>Example</th>
* <tr>
* <td><code>'.'yyyy-MM</code>
* <td>Rollover at the beginning of each month</td>
* <td>At midnight of May 31st, 2002 <code>/foo/bar.log</code> will be copied to <code>/foo/bar.log.2002-05</code>.
* Logging for the month of June will be output to <code>/foo/bar.log</code> until it is also rolled over the next
* month.
* <tr>
* <td><code>'.'yyyy-ww</code>
* <td>Rollover at the first day of each week. The first day of the week depends on the locale.</td>
* <td>Assuming the first day of the week is Sunday, on Saturday midnight, June 9th 2002, the file <i>/foo/bar.log</i>
* will be copied to <i>/foo/bar.log.2002-23</i>. Logging for the 24th week of 2002 will be output to
* <code>/foo/bar.log</code> until it is rolled over the next week.
* <tr>
* <td><code>'.'yyyy-MM-dd</code>
* <td>Rollover at midnight each day.</td>
* <td>At midnight, on March 8th, 2002, <code>/foo/bar.log</code> will be copied to
* <code>/foo/bar.log.2002-03-08</code>. Logging for the 9th day of March will be output to <code>/foo/bar.log</code>
* until it is rolled over the next day.
* <tr>
* <td><code>'.'yyyy-MM-dd-a</code>
* <td>Rollover at midnight and midday of each day.</td>
* <td>At noon, on March 9th, 2002, <code>/foo/bar.log</code> will be copied to
* <code>/foo/bar.log.2002-03-09-AM</code>. Logging for the afternoon of the 9th will be output to
* <code>/foo/bar.log</code> until it is rolled over at midnight.
* <tr>
* <td><code>'.'yyyy-MM-dd-HH</code>
* <td>Rollover at the top of every hour.</td>
* <td>At approximately 11:00.000 o'clock on March 9th, 2002, <code>/foo/bar.log</code> will be copied to
* <code>/foo/bar.log.2002-03-09-10</code>. Logging for the 11th hour of the 9th of March will be output to
* <code>/foo/bar.log</code> until it is rolled over at the beginning of the next hour.
* <tr>
* <td><code>'.'yyyy-MM-dd-HH-mm</code>
* <td>Rollover at the beginning of every minute.</td>
* <td>At approximately 11:23,000, on March 9th, 2001, <code>/foo/bar.log</code> will be copied to
* <code>/foo/bar.log.2001-03-09-10-22</code>. Logging for the minute of 11:23 (9th of March) will be output to
* <code>/foo/bar.log</code> until it is rolled over the next minute. </table>
* <p>
* Do not use the colon ":" character in anywhere in the <b>DatePattern</b> option. The text before the colon is
* interpeted as the protocol specificaion of a URL which is probably not what you want.
* @author Eirik Lygre
* @author Ceki Gülcü
*/
public class AdvancedDailyRollingFileAppender 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;

    /** Indicates if log files should be moved to archive file */
    private String compressBackups = "false";
    /** Indicates if archive file that may be created will be rolled off as it ages */
    private String rollCompressedBackups = "false";
    /** Maximum number of days to keep log files */
    private int maxNumberOfDays = 31;
    /** Number of days to wait before moving a log file to an archive */
    private int compressBackupsAfterDays = 31;
    /** Pattern used to name archive file (also controls what log files are grouped together */
    private String compressBackupsDatePattern = "'.'yyyy-MM";
    /** Maximum number of days to keep archive file before deleting */
    private int compressMaxNumberDays = 365;

    /**
     * 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 AdvancedDailyRollingFileAppender() {
    }

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

    /**
     * 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 String getCompressBackups() {
        return compressBackups;
    }

    public void setCompressBackups(String compressBackups) {
        this.compressBackups = compressBackups;
    }

    public String getMaxNumberOfDays() {
        return "" + maxNumberOfDays;
    }

    public void setMaxNumberOfDays(String days) {
        try {
            this.maxNumberOfDays = Integer.parseInt(days);
        } catch (Exception e) {
            // just leave it at default
        }

    }

    @Override
    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.getDefault());
        // 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...
    }

    /**
     * Rollover the current file to a new file.
     */
    void rollOver() 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.
            this.setFile(fileName, true, this.bufferedIO, this.bufferSize);
        } catch (IOException e) {
            errorHandler.error("setFile(" + fileName + ", true) call failed.");
        }
        scheduledFilename = datedFilename;
    }

    /**
     * This method differentiates AdvancedDailyRollingFileAppender 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.
     */
    @Override
    protected void subAppend(LoggingEvent event) {
        long n = System.currentTimeMillis();
        if (n >= nextCheck) {
            now.setTime(n);
            nextCheck = rc.getNextCheckMillis(now);
            try {
                cleanupAndRollOver();
            } catch (IOException ioe) {
                if (ioe instanceof InterruptedIOException) {
                    Thread.currentThread().interrupt();
                }
                LogLog.error("cleanupAndRollover() failed.", ioe);
            }
        }
        super.subAppend(event);
    }

    /*
     * This method checks to see if we're exceeding the number of log backups
     * that we are supposed to keep, and if so,
     * deletes the offending files. It then delegates to the rollover method to
     * rollover to a new file if required.
     */
    protected void cleanupAndRollOver() throws IOException {
        File file = new File(fileName);
        Calendar cal = Calendar.getInstance();

        cal.add(Calendar.DATE, -maxNumberOfDays);
        Date cutoffDate = cal.getTime();

        cal = Calendar.getInstance();
        cal.add(Calendar.DATE, -compressBackupsAfterDays);
        Date cutoffZip = cal.getTime();

        cal = Calendar.getInstance();
        cal.add(Calendar.DATE, -compressMaxNumberDays);
        Date cutoffDelZip = cal.getTime();

        if (file.getParentFile().exists()) {
            File[] files = file.getParentFile().listFiles(new StartsWithFileFilter(file.getName(), false));
            int nameLength = file.getName().length();
            for (int i = 0; i < files.length; i++) {
                String datePart = null;
                try {
                    datePart = files[i].getName().substring(nameLength);
                    Date date = sdf.parse(datePart);
                    // cutoffDate for deletion should be further back than
                    // cutoff for backup
                    if (date.before(cutoffDate)) {
                        files[i].delete();
                    } else if (getCompressBackups().equalsIgnoreCase("YES")
                            || getCompressBackups().equalsIgnoreCase("TRUE")) {
                        if (date.before(cutoffZip)) {
                            zipAndDelete(files[i], cutoffZip);
                        }
                    }
                } catch (ParseException pe) {
                    // Ignore - bad parse format, not a log file, current log
                    // file, or bad format on log file
                } catch (Exception e) {
                    LogLog.warn("Failed to process file " + files[i].getName(), e);
                }
                try {
                    if ((getRollCompressedBackups().equalsIgnoreCase("YES") || getRollCompressedBackups()
                            .equalsIgnoreCase("TRUE"))
                            && files[i].getName().endsWith(".zip")) {
                        datePart = files[i].getName().substring(nameLength, files[i].getName().length() - 4);
                        Date date = new SimpleDateFormat(compressBackupsDatePattern).parse(datePart);
                        if (date.before(cutoffDelZip)) {
                            files[i].delete();
                        }
                    }
                } catch (ParseException e) {
                    // Ignore - parse exceptions mean that format is wrong or
                    // there are other files in this dir
                } catch (Exception e) {
                    LogLog.warn("Evaluating archive file for rolling failed: " + files[i].getName(), e);
                }
            }
        }
        rollOver();
    }

    /**
     * Compresses the passed file to a .zip file, stores the .zip in the same directory as the passed file, and then
     * deletes the original, leaving only the .zipped archive.
     * @param file
     */
    private void zipAndDelete(File file, Date cutoffZip) throws IOException {
        if (!file.getName().endsWith(".zip")) {
            String rootLogFileName = new File(fileName).getName();
            String datePart = file.getName().substring(rootLogFileName.length());
            String fileRoot = file.getName().substring(0, file.getName().indexOf(datePart));
            SimpleDateFormat sdf = new SimpleDateFormat(getCompressBackupsDatePattern());
            String newFile = fileRoot + sdf.format(cutoffZip);
            File zipFile = new File(file.getParent(), newFile + ".zip");

            if (zipFile.exists()) {
                addFilesToExistingZip(zipFile, new File[] { file });
            } else {

                FileInputStream fis = new FileInputStream(file);
                FileOutputStream fos = new FileOutputStream(zipFile);
                ZipOutputStream zos = new ZipOutputStream(fos);
                ZipEntry zipEntry = new ZipEntry(file.getName());
                zos.putNextEntry(zipEntry);

                byte[] buffer = new byte[4096];
                while (true) {
                    int bytesRead = fis.read(buffer);
                    if (bytesRead == -1)
                        break;
                    else {
                        zos.write(buffer, 0, bytesRead);
                    }
                }
                zos.closeEntry();
                fis.close();
                zos.close();
            }
            file.delete();
        }
    }

    /**
     * This is used to add files to a zip that already exits.
     * @param zipFile
     * @param files
     * @throws IOException
     */
    public static void addFilesToExistingZip(File zipFile, File[] files) throws IOException {
        // get a temp file
        File tempFile = File.createTempFile(zipFile.getName(), null);
        // delete it, otherwise you cannot rename your existing zip to it.
        tempFile.delete();

        boolean renameOk = zipFile.renameTo(tempFile);
        if (!renameOk) {
            throw new RuntimeException("could not rename the file " + zipFile.getAbsolutePath() + " to "
                    + tempFile.getAbsolutePath());
        }
        byte[] buf = new byte[1024];

        ZipInputStream zin = new ZipInputStream(new FileInputStream(tempFile));
        ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFile));

        ZipEntry entry = zin.getNextEntry();
        while (entry != null) {
            String name = entry.getName();
            boolean notInFiles = true;
            for (File f : files) {
                if (f.getName().equals(name)) {
                    notInFiles = false;
                    break;
                }
            }
            if (notInFiles) {
                // Add ZIP entry to output stream.
                out.putNextEntry(new ZipEntry(name));
                // Transfer bytes from the ZIP file to the output file
                int len;
                while ((len = zin.read(buf)) > 0) {
                    out.write(buf, 0, len);
                }
            }
            entry = zin.getNextEntry();
        }
        // Close the streams
        zin.close();
        // Compress the files
        for (int i = 0; i < files.length; i++) {
            InputStream in = new FileInputStream(files[i]);
            // Add ZIP entry to output stream.
            out.putNextEntry(new ZipEntry(files[i].getName()));
            // Transfer bytes from the file to the ZIP file
            int len;
            while ((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
            // Complete the entry
            out.closeEntry();
            in.close();
        }
        // Complete the ZIP file
        out.close();
        tempFile.delete();
    }

    public String getCompressBackupsAfterDays() {
        return "" + compressBackupsAfterDays;
    }

    public void setCompressBackupsAfterDays(String days) {
        try {
            compressBackupsAfterDays = Integer.parseInt(days);
        } catch (Exception e) {
            // ignore - just use default
        }
    }

    public String getCompressBackupsDatePattern() {
        return compressBackupsDatePattern;
    }

    public void setCompressBackupsDatePattern(String pattern) {
        compressBackupsDatePattern = pattern;
    }

    public String getCompressMaxNumberDays() {
        return compressMaxNumberDays + "";
    }

    public void setCompressMaxNumberDays(String days) {
        try {
            this.compressMaxNumberDays = Integer.parseInt(days);
        } catch (Exception e) {
            // ignore - just use default
        }
    }

    public String getRollCompressedBackups() {
        return rollCompressedBackups;
    }

    public void setRollCompressedBackups(String rollCompressedBackups) {
        this.rollCompressedBackups = rollCompressedBackups;
    }

}

class StartsWithFileFilter implements FileFilter {
    private String startsWith;
    private boolean inclDirs = false;

    /**
     * 
     */
    public StartsWithFileFilter(String startsWith, boolean includeDirectories) {
        super();
        this.startsWith = startsWith.toUpperCase();
        inclDirs = includeDirectories;
    }

    /*
     * (non-Javadoc)
     * @see java.io.FileFilter#accept(java.io.File)
     */
    @Override
    public boolean accept(File pathname) {
        if (!inclDirs && pathname.isDirectory()) {
            return false;
        } else
            return pathname.getName().toUpperCase().startsWith(startsWith);
    }
}

/**
* RollingCalendar is a helper class to AdvancedDailyRollingFileAppender. Given a periodicity type and the current time,
* it computes the start of the next interval.
*/
class RollingCalendar extends GregorianCalendar {
    private static final long serialVersionUID = -3560331770601814177L;

    int type = AdvancedDailyRollingFileAppender.TOP_OF_TROUBLE;

    RollingCalendar() {
        super();
    }

    RollingCalendar(TimeZone tz, Locale locale) {
        super(tz, locale);
    }

    void setType(int type) {
        this.type = type;
    }

    public long getNextCheckMillis(Date now) {
        return getNextCheckDate(now).getTime();
    }

    public Date getNextCheckDate(Date now) {
        this.setTime(now);

        switch (type) {
            case AdvancedDailyRollingFileAppender.TOP_OF_MINUTE:
                this.set(Calendar.SECOND, 0);
                this.set(Calendar.MILLISECOND, 0);
                this.add(Calendar.MINUTE, 1);
                break;
            case AdvancedDailyRollingFileAppender.TOP_OF_HOUR:
                this.set(Calendar.MINUTE, 0);
                this.set(Calendar.SECOND, 0);
                this.set(Calendar.MILLISECOND, 0);
                this.add(Calendar.HOUR_OF_DAY, 1);
                break;
            case AdvancedDailyRollingFileAppender.HALF_DAY:
                this.set(Calendar.MINUTE, 0);
                this.set(Calendar.SECOND, 0);
                this.set(Calendar.MILLISECOND, 0);
                int hour = get(Calendar.HOUR_OF_DAY);
                if (hour < 12) {
                    this.set(Calendar.HOUR_OF_DAY, 12);
                } else {
                    this.set(Calendar.HOUR_OF_DAY, 0);
                    this.add(Calendar.DAY_OF_MONTH, 1);
                }
                break;
            case AdvancedDailyRollingFileAppender.TOP_OF_DAY:
                this.set(Calendar.HOUR_OF_DAY, 0);
                this.set(Calendar.MINUTE, 0);
                this.set(Calendar.SECOND, 0);
                this.set(Calendar.MILLISECOND, 0);
                this.add(Calendar.DATE, 1);
                break;
            case AdvancedDailyRollingFileAppender.TOP_OF_WEEK:
                this.set(Calendar.DAY_OF_WEEK, getFirstDayOfWeek());
                this.set(Calendar.HOUR_OF_DAY, 0);
                this.set(Calendar.MINUTE, 0);
                this.set(Calendar.SECOND, 0);
                this.set(Calendar.MILLISECOND, 0);
                this.add(Calendar.WEEK_OF_YEAR, 1);
                break;
            case AdvancedDailyRollingFileAppender.TOP_OF_MONTH:
                this.set(Calendar.DATE, 1);
                this.set(Calendar.HOUR_OF_DAY, 0);
                this.set(Calendar.MINUTE, 0);
                this.set(Calendar.SECOND, 0);
                this.set(Calendar.MILLISECOND, 0);
                this.add(Calendar.MONTH, 1);
                break;
            default:
                throw new IllegalStateException("Unknown periodicity type.");
        }
        return getTime();
    }
}

注:原作者的实现代码有两个bug: 如果设置7天, 结果只留下了6天的日志;另外一个就是在我的应用中发现删除文件的目录指定不对.另外一个就是删除只跟文件的名(比如这里的default.log)相关, 而跟生成的具体rotate log文件的格式(比如default.log.2012-01-12)是无关. 比如default.log.bak这样的文件也认为是7天文件中的一个. 

第二种实现方案: 我采用的方案
http://www.codeproject.com/KB/java/CustomDailyRollingFileApp.aspx 

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package logger;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.Serializable;
import java.net.URI;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;

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

/**
DailyRollingFileAppender extends {@link FileAppender} so that the
underlying file is rolled over at a user chosen frequency.

DailyRollingFileAppender has been observed to exhibit
synchronization issues and data loss.  The log4j extras
companion includes alternatives which should be considered
for new deployments and which are discussed in the documentation
for org.apache.log4j.rolling.RollingFileAppender.

<p>The rolling schedule is specified by the <b>DatePattern</b>
option. This pattern should follow the {@link SimpleDateFormat}
conventions. In particular, you <em>must</em> escape literal text
within a pair of single quotes. A formatted version of the date
pattern is used as the suffix for the rolled file name.

<p>For example, if the <b>File</b> option is set to
<code>/foo/bar.log</code> and the <b>DatePattern</b> set to
<code>'.'yyyy-MM-dd</code>, on 2001-02-16 at midnight, the logging
file <code>/foo/bar.log</code> will be copied to
<code>/foo/bar.log.2001-02-16</code> and logging for 2001-02-17
will continue in <code>/foo/bar.log</code> until it rolls over
the next day.

<p>Is is possible to specify monthly, weekly, half-daily, daily,
hourly, or minutely rollover schedules.

<p><table border="1" cellpadding="2">
<tr>
<th>DatePattern</th>
<th>Rollover schedule</th>
<th>Example</th>

<tr>
<td><code>'.'yyyy-MM</code>
<td>Rollover at the beginning of each month</td>

<td>At midnight of May 31st, 2002 <code>/foo/bar.log</code> will be
copied to <code>/foo/bar.log.2002-05</code>. Logging for the month
of June will be output to <code>/foo/bar.log</code> until it is
also rolled over the next month.

<tr>
<td><code>'.'yyyy-ww</code>

<td>Rollover at the first day of each week. The first day of the
week depends on the locale.</td>

<td>Assuming the first day of the week is Sunday, on Saturday
midnight, June 9th 2002, the file <i>/foo/bar.log</i> will be
copied to <i>/foo/bar.log.2002-23</i>.  Logging for the 24th week
of 2002 will be output to <code>/foo/bar.log</code> until it is
rolled over the next week.

<tr>
<td><code>'.'yyyy-MM-dd</code>

<td>Rollover at midnight each day.</td>

<td>At midnight, on March 8th, 2002, <code>/foo/bar.log</code> will
be copied to <code>/foo/bar.log.2002-03-08</code>. Logging for the
9th day of March will be output to <code>/foo/bar.log</code> until
it is rolled over the next day.

<tr>
<td><code>'.'yyyy-MM-dd-a</code>

<td>Rollover at midnight and midday of each day.</td>

<td>At noon, on March 9th, 2002, <code>/foo/bar.log</code> will be
copied to <code>/foo/bar.log.2002-03-09-AM</code>. Logging for the
afternoon of the 9th will be output to <code>/foo/bar.log</code>
until it is rolled over at midnight.

<tr>
<td><code>'.'yyyy-MM-dd-HH</code>

<td>Rollover at the top of every hour.</td>

<td>At approximately 11:00.000 o'clock on March 9th, 2002,
<code>/foo/bar.log</code> will be copied to
<code>/foo/bar.log.2002-03-09-10</code>. Logging for the 11th hour
of the 9th of March will be output to <code>/foo/bar.log</code>
until it is rolled over at the beginning of the next hour.


<tr>
<td><code>'.'yyyy-MM-dd-HH-mm</code>

<td>Rollover at the beginning of every minute.</td>

<td>At approximately 11:23,000, on March 9th, 2001,
<code>/foo/bar.log</code> will be copied to
<code>/foo/bar.log.2001-03-09-10-22</code>. Logging for the minute
of 11:23 (9th of March) will be output to
<code>/foo/bar.log</code> until it is rolled over the next minute.

</table>

<p>Do not use the colon ":" character in anywhere in the
<b>DatePattern</b> option. The text before the colon is interpeted
as the protocol specificaion of a URL which is probably not what
you want.


@author Eirik Lygre
@author Ceki Gülcü
<br/> <br/>
<b>Important Note:</b>
This is modified version of <code>DailyRollingFileAppender</code>. I have just added <code>maxBackupIndex</code>. So, if your number of log files increased more than <code>maxBackupIndex</code> it will delete the older log files.
The modified code only tested on Windows Operating System. If it have any issue on any other platform please modified it accordingly.
@ModifiedBy: Bikash Shaw
*/
public class CustomDailyRollingFileAppender 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 date pattern. By default, the pattern is set to
       "'.'yyyy-MM-dd" meaning daily rollover.
     */
    private String datePattern = "'.'yyyy-MM-dd";
    /**
    There is one backup file by default.
     */
    protected int maxBackupIndex = 1;

    /**
       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 CustomDailyRollingFileAppender() {
    }

    /**
      Instantiate a <code>DailyRollingFileAppender</code> and open the
      file designated by <code>filename</code>. The opened filename will
      become the ouput destination for this appender.

     */
    public CustomDailyRollingFileAppender(Layout layout, String filename,
            String datePattern) throws IOException {
        super(layout, filename, true);
        this.datePattern = datePattern;
        activateOptions();
    }

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

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

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

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

    @Override
    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.getDefault());
        // 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...
    }

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

        List<ModifiedTimeSortableFile> files = getAllFiles();
        Collections.sort(files);
        if (files.size() >= maxBackupIndex) {
            int index = 0;
            int diff = files.size() - (maxBackupIndex - 1);
            for (ModifiedTimeSortableFile file : files) {
                if (index >= diff)
                    break;

                file.delete();
                index++;
            }
        }

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

        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.
            this.setFile(fileName, true, this.bufferedIO, this.bufferSize);
        } catch (IOException e) {
            errorHandler.error("setFile(" + fileName + ", true) call failed.");
        }
        scheduledFilename = datedFilename;
    }

    /**
     * This method differentiates DailyRollingFileAppender 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.
     * */
    @Override
    protected void subAppend(LoggingEvent event) {
        long n = System.currentTimeMillis();
        if (n >= nextCheck) {
            now.setTime(n);
            nextCheck = rc.getNextCheckMillis(now);
            try {
                rollOver();
            } catch (IOException ioe) {
                if (ioe instanceof InterruptedIOException) {
                    Thread.currentThread().interrupt();
                }
                LogLog.error("rollOver() failed.", ioe);
            }
        }
        super.subAppend(event);
    }

    /**
     * This method searches list of log files
     * based on the pattern given in the log4j configuration file
     * and returns a collection 
     * @return List<ModifiedTimeSortableFile>
     */
    private List<ModifiedTimeSortableFile> getAllFiles() {
        List<ModifiedTimeSortableFile> files = new ArrayList<ModifiedTimeSortableFile>();
        FilenameFilter filter = new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                String directoryName = dir.getPath();
                LogLog.debug("directory name: " + directoryName);
                File file = new File(fileName);
                String perentDirectory = file.getParent();
                if (perentDirectory != null)
                {
                    String localFile = fileName.substring(directoryName.length());
                    return name.startsWith(localFile);
                }
                return name.startsWith(fileName);
            }
        };
        File file = new File(fileName);
        String perentDirectory = file.getParent();
        if (file.exists()) {
            if (file.getParent() == null) {
                String absolutePath = file.getAbsolutePath();
                perentDirectory = absolutePath.substring(0, absolutePath.lastIndexOf(fileName));

            }
        }
        File dir = new File(perentDirectory);
        String[] names = dir.list(filter);

        for (int i = 0; i < names.length; i++) {
            files.add(new ModifiedTimeSortableFile(dir + System.getProperty("file.separator") + names[i]));
        }
        return files;
    }
}

/**
* The Class ModifiedTimeSortableFile extends java.io.File class and
* implements Comparable to sort files list based upon their modified date
*/
class ModifiedTimeSortableFile extends File implements Serializable, Comparable<File> {
    private static final long serialVersionUID = 1373373728209668895L;

    public ModifiedTimeSortableFile(String parent, String child) {
        super(parent, child);
        // TODO Auto-generated constructor stub
    }

    public ModifiedTimeSortableFile(URI uri) {
        super(uri);
        // TODO Auto-generated constructor stub
    }

    public ModifiedTimeSortableFile(File parent, String child) {
        super(parent, child);
    }

    public ModifiedTimeSortableFile(String string) {
        super(string);
    }

    @Override
    public int compareTo(File anotherPathName) {
        long thisVal = this.lastModified();
        long anotherVal = anotherPathName.lastModified();
        return (thisVal < anotherVal ? -1 : (thisVal == anotherVal ? 0 : 1));
    }
}

/**
*  RollingCalendar is a helper class to DailyRollingFileAppender.
*  Given a periodicity type and the current time, it computes the
*  start of the next interval. 
* */
class RollingCalendar extends GregorianCalendar {
    private static final long serialVersionUID = -3560331770601814177L;

    int type = CustomDailyRollingFileAppender.TOP_OF_TROUBLE;

    RollingCalendar() {
        super();
    }

    RollingCalendar(TimeZone tz, Locale locale) {
        super(tz, locale);
    }

    void setType(int type) {
        this.type = type;
    }

    public long getNextCheckMillis(Date now) {
        return getNextCheckDate(now).getTime();
    }

    public Date getNextCheckDate(Date now) {
        this.setTime(now);

        switch (type) {
            case CustomDailyRollingFileAppender.TOP_OF_MINUTE:
                this.set(Calendar.SECOND, 0);
                this.set(Calendar.MILLISECOND, 0);
                this.add(Calendar.MINUTE, 1);
                break;
            case CustomDailyRollingFileAppender.TOP_OF_HOUR:
                this.set(Calendar.MINUTE, 0);
                this.set(Calendar.SECOND, 0);
                this.set(Calendar.MILLISECOND, 0);
                this.add(Calendar.HOUR_OF_DAY, 1);
                break;
            case CustomDailyRollingFileAppender.HALF_DAY:
                this.set(Calendar.MINUTE, 0);
                this.set(Calendar.SECOND, 0);
                this.set(Calendar.MILLISECOND, 0);
                int hour = get(Calendar.HOUR_OF_DAY);
                if (hour < 12) {
                    this.set(Calendar.HOUR_OF_DAY, 12);
                } else {
                    this.set(Calendar.HOUR_OF_DAY, 0);
                    this.add(Calendar.DAY_OF_MONTH, 1);
                }
                break;
            case CustomDailyRollingFileAppender.TOP_OF_DAY:
                this.set(Calendar.HOUR_OF_DAY, 0);
                this.set(Calendar.MINUTE, 0);
                this.set(Calendar.SECOND, 0);
                this.set(Calendar.MILLISECOND, 0);
                this.add(Calendar.DATE, 1);
                break;
            case CustomDailyRollingFileAppender.TOP_OF_WEEK:
                this.set(Calendar.DAY_OF_WEEK, getFirstDayOfWeek());
                this.set(Calendar.HOUR_OF_DAY, 0);
                this.set(Calendar.MINUTE, 0);
                this.set(Calendar.SECOND, 0);
                this.set(Calendar.MILLISECOND, 0);
                this.add(Calendar.WEEK_OF_YEAR, 1);
                break;
            case CustomDailyRollingFileAppender.TOP_OF_MONTH:
                this.set(Calendar.DATE, 1);
                this.set(Calendar.HOUR_OF_DAY, 0);
                this.set(Calendar.MINUTE, 0);
                this.set(Calendar.SECOND, 0);
                this.set(Calendar.MILLISECOND, 0);
                this.add(Calendar.MONTH, 1);
                break;
            default:
                throw new IllegalStateException("Unknown periodicity type.");
        }
        return getTime();
    }

}

这个解决方案就跟名字一样, 具有更多的高级功能, 比如压缩归档, 但是我不需要:( 不过它解决了第一种解决方案的问题, 比如只保留某种格式的文件. 

三:

log4j自带定制支持MaxBackupIndex的DailyRollingFileAppender 

地址:http://wiki.apache.org/logging-log4j/DailyRollingFileAppender

/*
 * Copyright 1999-2005 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */



package org.apache.log4j;

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

import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.spi.LoggingEvent;

/**
   DailyMaxRollingFileAppender extends {@link FileAppender} so that the
   underlying file is rolled over at a user chosen frequency.

   <p>The rolling schedule is specified by the <b>DatePattern</b>.
   This pattern should follow the {@link SimpleDateFormat} conventions.
   In particular, you <em>must</em> escape literal text within a pair
   of single quotes. A formatted version of the date pattern is used
   as the suffix for the rolled file name.

   <p>For example, if the <b>File</b> option is set to
   <code>/foo/bar.log</code> and the <b>DatePattern</b> set to
   <code>'.'yyyy-MM-dd</code>, on 2001-02-16 at midnight, the logging
   file <code>/foo/bar.log</code> will be copied to
   <code>/foo/bar.log.2001-02-16</code> and logging for 2001-02-17
   will continue in <code>/foo/bar.log</code> until it rolls over
   the next day.

   <p>Is is possible to specify monthly, weekly, half-daily, daily,
   hourly, or minutely rollover schedules.

   <p><table border="1" cellpadding="2">
   <tr>
   <th>DatePattern</th>
   <th>Rollover schedule</th>
   <th>Example</th>

   <tr>
   <td><code>'.'yyyy-MM</code>
   <td>Rollover at the beginning of each month</td>

   <td>At midnight of May 31st, 2002 <code>/foo/bar.log</code> will be
   copied to <code>/foo/bar.log.2002-05</code>. Logging for the month
   of June will be output to <code>/foo/bar.log</code> until it is
   also rolled over the next month.

   <tr>
   <td><code>'.'yyyy-ww</code>

   <td>Rollover at the first day of each week. The first day of the
   week depends on the locale.</td>

   <td>Assuming the first day of the week is Sunday, on Saturday
   midnight, June 9th 2002, the file <i>/foo/bar.log</i> will be
   copied to <i>/foo/bar.log.2002-23</i>.  Logging for the 24th week
   of 2002 will be output to <code>/foo/bar.log</code> until it is
   rolled over the next week.

   <tr>
   <td><code>'.'yyyy-MM-dd</code>

   <td>Rollover at midnight each day.</td>

   <td>At midnight, on March 8th, 2002, <code>/foo/bar.log</code> will
   be copied to <code>/foo/bar.log.2002-03-08</code>. Logging for the
   9th day of March will be output to <code>/foo/bar.log</code> until
   it is rolled over the next day.

   <tr>
   <td><code>'.'yyyy-MM-dd-a</code>

   <td>Rollover at midnight and midday of each day.</td>

   <td>At noon, on March 9th, 2002, <code>/foo/bar.log</code> will be
   copied to <code>/foo/bar.log.2002-03-09-AM</code>. Logging for the
   afternoon of the 9th will be output to <code>/foo/bar.log</code>
   until it is rolled over at midnight.

   <tr>
   <td><code>'.'yyyy-MM-dd-HH</code>

   <td>Rollover at the top of every hour.</td>

   <td>At approximately 11:00.000 o'clock on March 9th, 2002,
   <code>/foo/bar.log</code> will be copied to
   <code>/foo/bar.log.2002-03-09-10</code>. Logging for the 11th hour
   of the 9th of March will be output to <code>/foo/bar.log</code>
   until it is rolled over at the beginning of the next hour.


   <tr>
   <td><code>'.'yyyy-MM-dd-HH-mm</code>

   <td>Rollover at the beginning of every minute.</td>

   <td>At approximately 11:23,000, on March 9th, 2001,
   <code>/foo/bar.log</code> will be copied to
   <code>/foo/bar.log.2001-03-09-10-22</code>. Logging for the minute
   of 11:23 (9th of March) will be output to
   <code>/foo/bar.log</code> until it is rolled over the next minute.

   </table>

   <p>Do not use the colon ":" character in anywhere in the
   <b>DatePattern</b> option. The text before the colon is interpeted
   as the protocol specificaion of a URL which is probably not what
   you want.

   <p>You have also to define the maximum number of file are kept
   before the oldest is erased.</p>

   @author Eirik Lygre
   @author Ceki Gülcü
   @author Riccardo Nicosia;
*/
public class DailyMaxRollingFileAppender 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 date pattern. By default, the pattern is set to
     "'.'yyyy-MM-dd" meaning daily rollover.
   */
  private String datePattern = "'.'yyyy-MM-dd";

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

  /**
     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;

  RollingPastCalendar rpc = new RollingPastCalendar();

  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 DailyMaxRollingFileAppender()
  {}

  /**
    Instantiate a <code>DailyRollingFileAppender</code> and open the
    file designated by <code>filename</code>. The opened filename will
    become the ouput destination for this appender.

    */
  public DailyMaxRollingFileAppender (Layout layout, String filename,
                                   String datePattern)
  throws IOException
  {
    super(layout, filename, true);
    this.datePattern = datePattern;
    activateOptions();
  }

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

  /**
     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 renamed to the value of the
     scheduledFilename variable when the next interval is entered.
   */
  public void setMaxBackupIndex(int maxBackups)
  {
    this.maxBackupIndex = maxBackups;
  }

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

  public void activateOptions()
  {
    super.activateOptions();

    LogLog.debug("Max backup file kept: "+ maxBackupIndex + ".");

    if(datePattern != null && fileName != null)
    {
      now.setTime(System.currentTimeMillis());
      sdf = new SimpleDateFormat(datePattern);
      int type = computeCheckPeriod();
      printPeriodicity(type);
      rpc.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()
  {
    RollingPastCalendar rollingPastCalendar = new RollingPastCalendar(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);
        rollingPastCalendar.setType(i);
        Date next = new Date(rollingPastCalendar.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...
  }

  /**
     Rollover the current file to a new file.
  */
  void rollOver() 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);

      // 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 + dateBefore());

        if (file.exists())
          file.delete();
      }
    }
    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.
      this.setFile(fileName, false, this.bufferedIO, this.bufferSize);
    }
    catch(IOException e) {
      errorHandler.error("setFile("+fileName+", false) call failed.");
    }
    scheduledFilename = datedFilename;
  }

  private String dateBefore()
  {
    String dataAnte = "";


    if(datePattern != null)
    {
      SimpleDateFormat simpleDateFormat = new SimpleDateFormat(datePattern);

      dataAnte =  simpleDateFormat.format(new Date(rpc.getPastCheckMillis(new Date(), maxBackupIndex)));
    }

    return dataAnte;
  }

  /**
   * This method differentiates DailyRollingFileAppender 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 = rpc.getNextCheckMillis(now);

      try
      {
        rollOver();
      }
      catch(IOException ioe)
      {
        LogLog.error("rollOver() failed.", ioe);
      }
    }

    super.subAppend(event);
   }

  /*
   * DEBUG
  */
  public static void main(String args[])
  {
    DailyMaxRollingFileAppender dmrfa = new DailyMaxRollingFileAppender();

    dmrfa.setDatePattern("'.'yyyy-MM-dd-HH-mm");

    dmrfa.setFile("prova");

    System.out.println("dmrfa.getMaxBackupIndex():" + dmrfa.getMaxBackupIndex());

    dmrfa.activateOptions();

    for(int i = 0; i < 5; i++)
    {
      dmrfa.subAppend(null);

      try
      {
        Thread.sleep(60000);
      }
      catch (InterruptedException ex)
      {
      }

      System.out.println("Fine attesa");
    }
  }
}

/**
 *  RollingPastCalendar is a helper class to DailyMaxRollingFileAppender.
 *  Given a periodicity type and the current time, it computes the
 *  past maxBackupIndex date.
 * */
class RollingPastCalendar extends RollingCalendar
{
  RollingPastCalendar() {
    super();
  }

  RollingPastCalendar(TimeZone tz, Locale locale) {
    super(tz, locale);
  }

  public long getPastCheckMillis(Date now, int maxBackupIndex)
  {
    return getPastDate(now, maxBackupIndex).getTime();
  }

  public Date getPastDate(Date now, int maxBackupIndex)
  {
    this.setTime(now);

    switch(type)
    {
      case DailyRollingFileAppender.TOP_OF_MINUTE:
           this.set(Calendar.SECOND, this.get(Calendar.SECOND));
           this.set(Calendar.MILLISECOND, this.get(Calendar.MILLISECOND));
           this.set(Calendar.MINUTE, this.get(Calendar.MINUTE) - maxBackupIndex);
           break;

      case DailyRollingFileAppender.TOP_OF_HOUR:
           this.set(Calendar.MINUTE, this.get(Calendar.MINUTE));
           this.set(Calendar.SECOND, this.get(Calendar.SECOND));
           this.set(Calendar.MILLISECOND, this.get(Calendar.MILLISECOND));
           this.set(Calendar.HOUR_OF_DAY, this.get(Calendar.HOUR_OF_DAY) - maxBackupIndex);
           break;

      case DailyRollingFileAppender.HALF_DAY:
           this.set(Calendar.MINUTE, this.get(Calendar.MINUTE));
           this.set(Calendar.SECOND, this.get(Calendar.SECOND));
           this.set(Calendar.MILLISECOND, this.get(Calendar.MILLISECOND));
           int hour = get(Calendar.HOUR_OF_DAY);
           if(hour < 12)
           {
             this.set(Calendar.HOUR_OF_DAY, 12);
           }
           else
           {
             this.set(Calendar.HOUR_OF_DAY, 0);
           }
           this.set(Calendar.DAY_OF_MONTH, this.get(Calendar.DAY_OF_MONTH) - maxBackupIndex);

           break;

      case DailyRollingFileAppender.TOP_OF_DAY:
           this.set(Calendar.HOUR_OF_DAY, this.get(Calendar.HOUR_OF_DAY));
           this.set(Calendar.MINUTE, this.get(Calendar.MINUTE));
           this.set(Calendar.SECOND, this.get(Calendar.SECOND));
           this.set(Calendar.MILLISECOND, this.get(Calendar.MILLISECOND));
           this.set(Calendar.DATE, this.get(Calendar.DATE) - maxBackupIndex);
           break;

      case DailyRollingFileAppender.TOP_OF_WEEK:
           this.set(Calendar.DAY_OF_WEEK, getFirstDayOfWeek());
           this.set(Calendar.HOUR_OF_DAY, this.get(Calendar.HOUR_OF_DAY));
           this.set(Calendar.MINUTE, this.get(Calendar.MINUTE));
           this.set(Calendar.SECOND, this.get(Calendar.SECOND));
           this.set(Calendar.MILLISECOND, this.get(Calendar.MILLISECOND));
           this.set(Calendar.WEEK_OF_YEAR, this.get(Calendar.WEEK_OF_YEAR) - maxBackupIndex);
           break;

      case DailyRollingFileAppender.TOP_OF_MONTH:
           this.set(Calendar.DATE, this.get(Calendar.DATE));
           this.set(Calendar.HOUR_OF_DAY, this.get(Calendar.HOUR_OF_DAY));
           this.set(Calendar.MINUTE, this.get(Calendar.MINUTE));
           this.set(Calendar.SECOND, this.get(Calendar.SECOND));
           this.set(Calendar.MILLISECOND, this.get(Calendar.MILLISECOND));
           this.set(Calendar.MONTH, this.get(Calendar.MONTH) - maxBackupIndex);
           break;

      default:
           throw new IllegalStateException("Unknown periodicity type.");
    }

    return getTime();
  }
}


SLF4J是一个抽象层,它并不提供具体的日志实现。因此,您需要结合具体的日志实现来实现日志的自动删除。 假设您使用的是logback作为具体的日志实现,那么您可以通过logback.xml配置文件来设置日志的自动删除。具体实现方法如下: ```xml <configuration> <!-- 定义appender --> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>application.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 按天归档 --> <fileNamePattern>application.%d{yyyy-MM-dd}.log</fileNamePattern> <!-- 保留30天的日志 --> <maxHistory>30</maxHistory> <!-- 设置删除历史日志的最大天数 --> <totalSizeCap>1GB</totalSizeCap> <cleanHistoryOnStart>true</cleanHistoryOnStart> <maxFileSize>10MB</maxFileSize> </rollingPolicy> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <!-- 定义日志输出级别 --> <root level="info"> <appender-ref ref="FILE" /> </root> </configuration> ``` 在上述配置中,我们使用了TimeBasedRollingPolicy按天归档日志,并设置了保留30天的日志。同时,我们设置了删除历史日志的最大天数为1GB,意味着当历史日志总大小超过1GB时,将会自动删除。此外,我们还设置了每个日志文件最大为10MB,并且在应用启动时清空历史日志。 希望能够帮到您!如果您还有其他问题,欢迎随时问我。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值