使用log4j實現保留N天內的日誌
阿新 • • 發佈:2019-02-12
由於專案後期要根據日誌進行查詢原因,然後伺服器的磁碟空間有限,不能永久保留日誌,而且日誌檔案也會隨著時間的推移變的越來越大.
於是需要保留一個月或者半個月的日誌,但是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個小時,你想保留幾天的就設定這個引數即可.
以下是檔案下載地址: