1. 程式人生 > >使用log4j實現保留N天內的日誌

使用log4j實現保留N天內的日誌

由於專案後期要根據日誌進行查詢原因,然後伺服器的磁碟空間有限,不能永久保留日誌,而且日誌檔案也會隨著時間的推移變的越來越大.

於是需要保留一個月或者半個月的日誌,但是log4j原始碼org.apache.log4j.DailyRollingFileAppender並不能完美的實現,

只能自己重寫log4j的org.apache.log4j.DailyRollingFileAppender  以下是改寫後的原始碼

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";

	private int  maxBackupIndex  = 1;

	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;

	/**
	 * 獲取當前環境所處的時區
	 * 僅供computeCheckPeriod方法使用
	 */
	static final TimeZone gmtTimeZone = TimeZone.getTimeZone("GMT");

	public CustomDailyRollingFileAppender() {}

	public CustomDailyRollingFileAppender(Layout layout, String filename, String datePattern) throws IOException {
		super(layout, filename, true);

		this.datePattern = datePattern;
		activateOptions();
	}

	public void setDatePattern(String pattern) {
		this.datePattern = pattern;
	}

	public String getDatePattern() {
		return datePattern;
	}

	public int getMaxBackupIndex() {
		return maxBackupIndex;
	}

	public void setMaxBackupIndex(int maxBackupIndex) {
		this.maxBackupIndex = maxBackupIndex;
	}

	/**
	 * activateOptions譯名為啟用操作
	 * 意思是按照配置的引數進行初始化
	 * scheduledFilename為log的最後一次修改時間
	 */
	@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+"].");
		}
	}

	/**
	 * 根據type列印做出日誌列印
	 * @param type
	 */
	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());
		//設定初始時間為格林尼治時間: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);

				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...
	}

	/**
	 * 按照週期將當前日誌檔案轉存為日期檔案
	 *
	 * @throws IOException
	 */
	void rollOver() throws IOException {

		if (datePattern == null) {
			errorHandler.error("Missing DatePattern option in rollOver().");
			return;
		}

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

		//如果最後一次的修改時間為當前時間 ,則不做任何任何操作
		if (scheduledFilename.equals(datedFilename)) {
			return;
		}

		// 關閉當前檔案,重新命名為日期檔案
		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+"].");
		}

		//獲取日誌檔案列表,控制數量,實現清理策略
		if (file.getParentFile().exists()){
			File[] files = file.getParentFile().listFiles(new LogFileFilter(file.getName()));
			Long[] dateArray = new Long[files.length];
			for (int i = 0; i < files.length; i++) {
				File fileItem = files[i];
				String fileDateStr = fileItem.getName().replace(file.getName(), "");
				Date filedate = null;
				try {
					filedate = sdf.parse(fileDateStr);
					long fileDateLong = filedate.getTime();
					dateArray[i] = fileDateLong;
				} catch (ParseException e) {
					LogLog.error("Parse File Date Throw Exception : " + e.getMessage());
				}
			}
			Arrays.sort(dateArray);
			if (dateArray.length > maxBackupIndex) {
				for (int i = 0; i < dateArray.length - maxBackupIndex; i++) {
					String dateFileName = file.getPath() + sdf.format(dateArray[i]);
					File dateFile = new File(dateFileName);
					if (dateFile.exists()) {
						dateFile.delete();
					}
				}
			}
		}

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

	/**
	 * 寫入日誌之前判斷是否需要新起一個日誌來記錄
	 */
	@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);
	}
}

/**
 * 檔案過濾器
 *
 * @author Mike.Peng
 * @time Jan 12, 2016  5:28:31 PM
 */
class LogFileFilter implements FileFilter {
	private String logName;

	public LogFileFilter(String logName) {
		this.logName = logName;
	}

	@Override
	public boolean accept(File file) {
		if (logName == null || file.isDirectory()) {
			return false;
		} else {
			LogLog.debug(file.getName());
			return file.getName().startsWith(logName);
		}
	}
}

/**
 * CustomDailyRollingFileAppender的內部類
 * 提供週期型別和當前時間 ,計算並返回下一個週期的開始時間
 *
 * @author Mike.Peng
 * @time Jan 12, 2016  5:28:48 PM
 */
class RollingCalendar extends GregorianCalendar {
	private static final long serialVersionUID = -3560331770601814177L;

	int type = CustomDailyRollingFileAppender.TOP_OF_TROUBLE;

	/**
	 * RollingCalendar預設構造器
	 */
	RollingCalendar() {
		super();
	}

	/**
	 * RollingCalendar構造器
	 * 根據地點時區 ,獲取對應的日曆Calendar
	 * @param tz
	 * @param locale
	 */
	RollingCalendar(TimeZone tz, Locale locale) {
		super(tz, locale);
	}

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

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

	/**
	 * 根據所傳入的時間以及時間型別獲取下一個時間
	 * @param now
	 * @return
	 */
	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.properties檔案
log4j.rootLogger=DEBUG,D,consol

log4j.appender.consol=org.apache.log4j.ConsoleAppender
log4j.appender.consol.layout=org.apache.log4j.PatternLayout
log4j.appender.consol.Threshold = debug
log4j.appender.consol.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} - [ %p ]  %m%n
# cm.sh.lx.acc.util.CustomDailyRollingFileAppender
# org.apache.log4j.DailyRollingFileAppender
log4j.appender.D = cm.sh.lx.acc.util.CustomDailyRollingFileAppender
log4j.appender.D.File =${user.dir}/../uamlogs/ctg_applog_.log
log4j.appender.D.datePattern = yyyy-MM-dd-HH'.'
log4j.appender.D.maxBackupIndex=48
log4j.appender.D.Append = true
log4j.appender.D.Threshold = info
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern =%-d{yyyyMMddHHmmss:S}%m%n

log4j.logger.org.springframework=WARN
log4j.logger.com.ctg=info
log4j.logger.org.apache=WARN
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

maxBackupIndex就是保留多少個檔案,  我是按照每小時進行生成一個檔案,這樣一天24個小時,你想保留幾天的就設定這個引數即可.

以下是檔案下載地址: