package org.apache.log4j; import java.text.*; import java.io.*; import org.apache.log4j.helpers.*; import java.util.*; import java.util.zip.GZIPOutputStream; import org.apache.log4j.spi.*; /** * 替换log4j包里的DailyRollingFileAppender类, * 增加文件压缩流程, * @author xu.zhou * @date 20211108 */ public class CompressDailyRollingFileAppender 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; private String datePattern; private String scheduledFilename; private long nextCheck; Date now; SimpleDateFormat sdf; RollingCalendar rc; int checkPeriod; static final TimeZone gmtTimeZone; public CompressDailyRollingFileAppender() { this.datePattern = "'.'yyyy-MM-dd"; this.nextCheck = System.currentTimeMillis() - 1L; this.now = new Date(); this.rc = new RollingCalendar(); this.checkPeriod = -1; } public CompressDailyRollingFileAppender(final Layout layout, final String filename, final String datePattern) throws IOException { super(layout, filename, true); this.datePattern = "'.'yyyy-MM-dd"; this.nextCheck = System.currentTimeMillis() - 1L; this.now = new Date(); this.rc = new RollingCalendar(); this.checkPeriod = -1; this.datePattern = datePattern; this.activateOptions(); } public void setDatePattern(final String pattern) { this.datePattern = pattern; } public String getDatePattern() { return this.datePattern; } public void activateOptions() { super.activateOptions(); if (this.datePattern != null && super.fileName != null) { this.now.setTime(System.currentTimeMillis()); this.sdf = new SimpleDateFormat(this.datePattern); final int type = this.computeCheckPeriod(); this.printPeriodicity(type); this.rc.setType(type); final File file = new File(super.fileName); this.scheduledFilename = super.fileName + this.sdf.format(new Date(file.lastModified())); } else { LogLog.error("Either File or DatePattern options are not set for appender [" + super.name + "]."); } } void printPeriodicity(final int type) { switch (type) { case 0: { LogLog.debug("Appender [" + super.name + "] to be rolled every minute."); break; } case 1: { LogLog.debug("Appender [" + super.name + "] to be rolled on top of every hour."); break; } case 2: { LogLog.debug("Appender [" + super.name + "] to be rolled at midday and midnight."); break; } case 3: { LogLog.debug("Appender [" + super.name + "] to be rolled at midnight."); break; } case 4: { LogLog.debug("Appender [" + super.name + "] to be rolled at start of week."); break; } case 5: { LogLog.debug("Appender [" + super.name + "] to be rolled at start of every month."); break; } default: { LogLog.warn("Unknown periodicity for appender [" + super.name + "]."); break; } } } int computeCheckPeriod() { final RollingCalendar rollingCalendar = new RollingCalendar(CompressDailyRollingFileAppender.gmtTimeZone, Locale.ENGLISH); final Date epoch = new Date(0L); if (this.datePattern != null) { for (int i = 0; i <= 5; ++i) { final SimpleDateFormat simpleDateFormat = new SimpleDateFormat(this.datePattern); simpleDateFormat.setTimeZone(CompressDailyRollingFileAppender.gmtTimeZone); final String r0 = simpleDateFormat.format(epoch); rollingCalendar.setType(i); final Date next = new Date(rollingCalendar.getNextCheckMillis(epoch)); final String r2 = simpleDateFormat.format(next); if (r0 != null && r2 != null && !r0.equals(r2)) { return i; } } } return -1; } /** * 日志滚动 * @throws IOException */ void rollOver() throws IOException { if (this.datePattern == null) { super.errorHandler.error("Missing DatePattern option in rollOver()."); return; } String dateStr = this.sdf.format(this.now); final String datedFilename = super.fileName + dateStr; //要删除的文件是否与最后的日志文件相同,说明此文件还不要处理,正在写入 if (this.scheduledFilename.equals(datedFilename)) { return; } this.closeFile(); //生成要滚动的文件 final File target = new File(this.scheduledFilename); if (target.exists()) { //System.out.println("删除文件=>"+target.getName()); target.delete(); } //System.out.println("datePattern=>"+datePattern); //System.out.println("datedFilename=>"+datedFilename); //System.out.println("scheduledFilename=>"+this.scheduledFilename); //System.out.println("super.fileName=>"+super.fileName); final File file = new File(super.fileName); //把正在写入的日志文件更名为带日期的日志文件 final boolean result = file.renameTo(target); if (result) { LogLog.debug(super.fileName + " -> " + this.scheduledFilename); } else { LogLog.error("Failed to rename [" + super.fileName + "] to [" + this.scheduledFilename + "]."); } try { this.setFile(super.fileName, false, super.bufferedIO, super.bufferSize); } catch (IOException e) { super.errorHandler.error("setFile(" + super.fileName + ", false) call failed."); } //压缩日志后删除文件 zipfile(this.scheduledFilename); this.scheduledFilename = datedFilename; } /** * 压缩日志后删除文件 * @param fileName */ private void zipfile(String fileName){ //System.out.println("zipfile.fileName========================"+fileName); File file = new File(fileName); //creat zip output stream to build zip file GZIPOutputStream gzout = null; FileInputStream fin = null; byte[] buf = new byte[1024]; //file -> gz try { fin = new FileInputStream(file); gzout = new GZIPOutputStream(new FileOutputStream(fileName + ".gz")); int num; while ((num = fin.read(buf, 0, buf.length)) != -1) { gzout.write(buf, 0, num); } gzout.flush(); gzout.finish(); LogLog.debug(fileName + " -> " + fileName + ".gz" + " successful!"); } catch (IOException e) { LogLog.error("add gz file(" + fileName + ".gz" + ") failed."); } finally { try { if (gzout != null) { gzout.close(); } if (fin != null) fin.close(); } catch (IOException e) { LogLog.error("close Stream failed"); } } //delete old file file.delete(); } protected void subAppend(final LoggingEvent event) { final long n = System.currentTimeMillis(); if (n >= this.nextCheck) { this.now.setTime(n); this.nextCheck = this.rc.getNextCheckMillis(this.now); try { this.rollOver(); } catch (IOException ioe) { LogLog.error("rollOver() failed.", ioe); } } super.subAppend(event); } static { gmtTimeZone = TimeZone.getTimeZone("GMT"); } }