定时删除超过N天前的日志文件

it2026-08-19  5

流程:     代码: package com.han.log4jplugins;   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;   /** * @项目名称:log4jplugins * @类名称:CustomDailyRollingFileAppender * @类描述:DailyRollingFileAppender extends FileAppender</br> * 重写RollingFileAppender类</br> * 实现maxBackupIndex限制日志文件数量功能(定时删除超过N天前的日志文件) * * @UserGuid 分log4j.xml或log4j.properties两种情况:<br> * 1)log4j.xml:设置<appender name="FILE" class="org.apache.log4j.RollingFileAppender">中的class属性为:com.han.log4jplugins.CustomDailyRollingFileAppender;<br> * 2)log4j.properties:替换log4j.appender.debug=org.apache.log4j.RollingFileAppender中的RollingFileAppender,设置为com.han.log4jplugins.CustomDailyRollingFileAppender。 * * @author HanZhijun * @version V1.0.0 * @since 2018-03-01 */ public class CustomDailyRollingFileAppender extends FileAppender {   // 初始化参数 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;   /** * 生产日志文件后缀("'.'yyyy-MM-dd") */ private String datePattern = "'.'yyyy-MM-dd";   /** * 默认:1个日志文件 */ protected int maxBackupIndex = 1;   /** * 保存前一天的日志文件名称 =filename+("'.'yyyy-MM-dd") */ 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");   // 无参构造 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 thisappender. * * @param layout * @param filename */ public CustomDailyRollingFileAppender(Layout layout, String filename, String datePattern) throws IOException { super(layout, filename, true); this.datePattern = datePattern; activateOptions(); }   public void setDatePattern(String pattern) { datePattern = pattern; }   public void setMaxBackupIndex(int maxBackups) { this.maxBackupIndex = maxBackups; }   public int getMaxBackupIndex() { return maxBackupIndex; }   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("EitherFile 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("Unknownperiodicity 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("MissingDatePattern 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("Failedto 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 differentiatesDailyRollingFileAppender from its super class. * <p> * Before actually logging, thismethod will check whether it is time to do a rollover. If it is, * it willschedule 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 log4jconfiguration file * * and returns a collection * * @returnList&lt;ModifiedTimeSortableFile&gt; * */ private List<ModifiedTimeSortableFile> getAllFiles() { List<ModifiedTimeSortableFile> files = new ArrayList<ModifiedTimeSortableFile>();   FilenameFilter filter = new FilenameFilter() { // 重写accept方法,确认是否是同一项目日志文件名称前缀。   @Override public boolean accept(File dir, String name) { String directoryName = dir.getPath(); LogLog.debug("directoryname: " + directoryName); File file = new File(fileName); String perentDirectory = file.getParent();   if (perentDirectory != null) { // name=demo.log // directoryName= /logs , // 直接fileName.substring(directoryName.length());切割之后的localFile=/demo.log…,会导致name.startsWith(localFile)始终是false // so解决方案:长度 +1 String localFile = fileName.substring(directoryName.length() + 1); 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;   }   }   /** * 自定义file类,重写compareTo方法,比较文件的修改时间 * * The ClassModifiedTimeSortableFile extends java.io.File class and * * implements Comparable to sortfiles 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); }   public ModifiedTimeSortableFile(URI uri) { super(uri); }   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 toDailyRollingFileAppender. * * Given a periodicity type and the currenttime, it computes the * * start of the next interval. * */ class RollingCalendar extends GregorianCalendar { private static final long serialVersionUID = -8295691444111406775L; 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配置文件的设置: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd" > <log4j:configuration> <!-- 引用自定义的CustomDailyRollingFileAppender日志类 --> <appender name="FILE" class="com.han.log4jplugins.CustomDailyRollingFileAppender"> <!-- 自定义日志输出路径和名称 --> <param name="file" value="D:/logs/test-agent.log"/>   <!-- 自定义历史日志文件后缀名 --> <param name="datePattern" value="'.'yyyy-MM-dd"/>   <!-- 自定义日志文件总数量,大于这个数量,自动删除 --> <param name="maxBackupIndex" value="1"/> <param name="append" value="true"/> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%d [%t] %p - %m%n"/> </layout> </appender>   <appender name="stdout" class="org.apache.log4j.ConsoleAppender"> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss,SSS} %p [%c-%M-%L]:%m%n" /> </layout> </appender>   <root> <priority value="info"/> <appender-ref ref="FILE"/> <appender-ref ref="stdout"/> </root> </log4j:configuration>   功能测试类: package com.han.log4jplugins;   import java.io.File; import java.net.URL;   import org.apache.log4j.Logger; import org.apache.log4j.PropertyConfigurator;   /** * 自定义每天日志文件生成策略的测试类 * * @author HanZhijun * @version V1.0.0 * @since 2018-03-01 */ public class CustomDailyRollingFileAppenderTest { private static Logger logger = Logger.getLogger(CustomDailyRollingFileAppenderTest.class);   public static void main(String[] args) { String configFile = "log4j.xml"; final File file = new File(configFile); if(!file.exists()) { final URL url = CustomDailyRollingFileAppenderTest.class.getClassLoader().getResource("log4j.xml"); configFile = url.getPath(); }   PropertyConfigurator.configure(configFile); logger.info("11111"); }   }

转载于:https://www.cnblogs.com/sinoknots/p/8487104.html

相关资源:数据结构—成绩单生成器
最新回复(0)