您好,登錄后才能下訂單哦!
最近要實現定期刪除N天前的日志需求:
Log4j作為常用的日志生成工具,其清除日志的策略卻十分有限。只有在RollingFileAppender中可以通過設置MaxFileSize和maxBackupIndex屬性來指定要保留的日志文件大小以及個數,從而實現自動清除。
但是實際生產中,我們的真實的需求常常是定時每天生成一個日志文件,然后保留最近幾天或近幾個月的日志,歷史日志需要及時清理。可是Log4j中的DailyRollingFileAppender這個類卻不帶屬性maxBackupIndex,maxFileSize等,所以無法通過直接配置實現。
針對這種情況,可以是寫一個定時刪除日志的腳本,也可以是重新實現DailyRollingFileAppender類
這里我們通過繼承FileAppender,重新實現DailyRollingFileAppender類,帶有按時間順序刪除N天前的日志文件的功能。
package com.demo.filter;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
importjava.io.InterruptedIOException;
import java.io.Serializable;
import java.net.URI;
importjava.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
importjava.util.GregorianCalendar;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
importorg.apache.log4j.FileAppender;
import org.apache.log4j.Layout;
importorg.apache.log4j.helpers.LogLog;
importorg.apache.log4j.spi.LoggingEvent;
/**
*
* @項目名稱:wupao-job
* @類名稱:CustomDailyRollingFileAppender
* @類描述:DailyRollingFileAppender extendsFileAppender
* </br>重寫RollingFileAppender類
* </br>實現maxBackupIndex限制日志文件數量功能(定時刪除超過N天前的日志文件)
* @創建人:wyait
* @創建時間:2017年8月8日下午2:40:38
* @version:1.0.0
*/
public classCustomDailyRollingFileAppender extends FileAppender {
//初始化參數
staticfinal int TOP_OF_TROUBLE = -1;
staticfinal int TOP_OF_MINUTE = 0;
staticfinal int TOP_OF_HOUR = 1;
staticfinal int HALF_DAY = 2;
staticfinal int TOP_OF_DAY = 3;
staticfinal int TOP_OF_WEEK = 4;
staticfinal int TOP_OF_MONTH = 5;
/**
*生產日志文件后綴("'.'yyyy-MM-dd")
*/
privateString datePattern = "'.'yyyy-MM-dd";
/**
* 默認:1個日志文件
*/
protectedint maxBackupIndex = 1;
/**
* 保存前一天的日志文件名稱 =filename+("'.'yyyy-MM-dd")
*/
privateString scheduledFilename;
//The next time we estimate a rollover should occur.
privatelong nextCheck = System.currentTimeMillis() - 1;
Datenow = new Date();
SimpleDateFormatsdf;
RollingCalendarrc = new RollingCalendar();
intcheckPeriod = TOP_OF_TROUBLE;
//The gmtTimeZone is used only in computeCheckPeriod() method.
staticfinal TimeZone gmtTimeZone = TimeZone.getTimeZone("GMT");
//無參構造
publicCustomDailyRollingFileAppender() {
}
/**有參構造
Instantiate a<code>DailyRollingFileAppender</code> and open the
file designated by<code>filename</code>. The opened filename will
become the ouput destination for thisappender.
*/
publicCustomDailyRollingFileAppender(Layout layout, String filename,
StringdatePattern) throws IOException {
super(layout,filename, true);
this.datePattern= datePattern;
activateOptions();
}
publicvoid setDatePattern(String pattern) {
datePattern= pattern;
}
publicvoid setMaxBackupIndex(int maxBackups) {
this.maxBackupIndex= maxBackups;
}
publicint getMaxBackupIndex() {
returnmaxBackupIndex;
}
publicString getDatePattern() {
returndatePattern;
}
@Override
publicvoid activateOptions() {
super.activateOptions();
if(datePattern != null && fileName != null) {
now.setTime(System.currentTimeMillis());
sdf= new SimpleDateFormat(datePattern);
inttype = computeCheckPeriod();
printPeriodicity(type);
rc.setType(type);
Filefile = new File(fileName);
scheduledFilename= fileName
+sdf.format(new Date(file.lastModified()));
}else {
LogLog.error("EitherFile or DatePattern options are not set for appender ["
+name + "].");
}
}
voidprintPeriodicity(int type) {
switch(type) {
caseTOP_OF_MINUTE:
LogLog.debug("Appender[" + name + "] to be rolled every minute.");
break;
caseTOP_OF_HOUR:
LogLog.debug("Appender[" + name
+"] to be rolled on top of every hour.");
break;
caseHALF_DAY:
LogLog.debug("Appender[" + name
+"] to be rolled at midday and midnight.");
break;
caseTOP_OF_DAY:
LogLog.debug("Appender[" + name + "] to be rolled at midnight.");
break;
caseTOP_OF_WEEK:
LogLog.debug("Appender[" + name
+"] to be rolled at start of week.");
break;
caseTOP_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).
intcomputeCheckPeriod() {
RollingCalendarrollingCalendar = new RollingCalendar(gmtTimeZone,
Locale.getDefault());
//set sate to 1970-01-01 00:00:00 GMT
Dateepoch = new Date(0);
if(datePattern != null) {
for(int i = TOP_OF_MINUTE; i <= TOP_OF_MONTH; i++) {
SimpleDateFormatsimpleDateFormat = new SimpleDateFormat(
datePattern);
simpleDateFormat.setTimeZone(gmtTimeZone);// do all date
//formatting in GMT
Stringr0 = simpleDateFormat.format(epoch);
rollingCalendar.setType(i);
Datenext = new Date(rollingCalendar.getNextCheckMillis(epoch));
Stringr1 = simpleDateFormat.format(next);
//System.out.println("Type = "+i+", r0 = "+r0+", r1 ="+r1);
if(r0 != null && r1 != null && !r0.equals(r1)) {
returni;
}
}
}
returnTOP_OF_TROUBLE; // Deliberately head for trouble...
}
/**核心方法:生成日志文件,并完成對備份數量的監測以及歷史日志的刪除
Rollover the current file to a new file.
*/
voidrollOver() throws IOException {
//獲取所有日志歷史文件:并完成對備份數量的監測以及歷史日志的刪除
List<ModifiedTimeSortableFile>files = getAllFiles();
Collections.sort(files);
if(files.size() >= maxBackupIndex) {
intindex = 0;
intdiff = 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);
StringdatedFilename = 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();
Filetarget = new File(scheduledFilename);
if(target.exists()) {
target.delete();
}
Filefile = new File(fileName);
booleanresult = 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
protectedvoid subAppend(LoggingEvent event) {
longn = 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<ModifiedTimeSortableFile>
*/
privateList<ModifiedTimeSortableFile> getAllFiles() {
List<ModifiedTimeSortableFile>files = new ArrayList<ModifiedTimeSortableFile>();
FilenameFilterfilter = new FilenameFilter() {
//重寫accept方法,確認是否是同一項目日志文件名稱前綴。
@Override
publicboolean accept(File dir, String name) {
StringdirectoryName = dir.getPath();
LogLog.debug("directoryname: " + directoryName);
Filefile = new File(fileName);
StringperentDirectory = file.getParent();
if(perentDirectory != null) {
//name=demo.log
//directoryName= /logs ,
//直接fileName.substring(directoryName.length());切割之后的localFile=/demo.log…,會導致name.startsWith(localFile)始終是false
//so解決方案:長度 +1
StringlocalFile = fileName.substring(directoryName
.length()+ 1);
returnname.startsWith(localFile);
}
returnname.startsWith(fileName);
}
};
Filefile = new File(fileName);
StringperentDirectory = file.getParent();
if(file.exists()) {
if(file.getParent() == null) {
StringabsolutePath = file.getAbsolutePath();
perentDirectory= absolutePath.substring(0,
absolutePath.lastIndexOf(fileName));
}
}
Filedir = new File(perentDirectory);
String[]names = dir.list(filter);
for(int i = 0; i < names.length; i++) {
files.add(newModifiedTimeSortableFile(dir
+System.getProperty("file.separator") + names[i]));
}
returnfiles;
}
}
/** 自定義file類,重寫compareTo方法,比較文件的修改時間
* The ClassModifiedTimeSortableFile extends java.io.File class and
* implements Comparable to sortfiles list based upon their modified date
*/
class ModifiedTimeSortableFileextends File implements Serializable,
Comparable<File>{
privatestatic final long serialVersionUID = 1373373728209668895L;
publicModifiedTimeSortableFile(String parent, String child) {
super(parent,child);
//TODO Auto-generated constructor stub
}
publicModifiedTimeSortableFile(URI uri) {
super(uri);
//TODO Auto-generated constructor stub
}
publicModifiedTimeSortableFile(File parent, String child) {
super(parent,child);
}
publicModifiedTimeSortableFile(String string) {
super(string);
}
@Override
publicint compareTo(File anotherPathName) {
longthisVal = this.lastModified();
longanotherVal = 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 extendsGregorianCalendar {
privatestatic final long serialVersionUID = -8295691444111406775L;
inttype = CustomDailyRollingFileAppender.TOP_OF_TROUBLE;
RollingCalendar(){
super();
}
RollingCalendar(TimeZonetz, Locale locale) {
super(tz,locale);
}
voidsetType(int type) {
this.type= type;
}
publiclong getNextCheckMillis(Date now) {
returngetNextCheckDate(now).getTime();
}
publicDate getNextCheckDate(Date now) {
this.setTime(now);
switch(type) {
caseCustomDailyRollingFileAppender.TOP_OF_MINUTE:
this.set(Calendar.SECOND,0);
this.set(Calendar.MILLISECOND,0);
this.add(Calendar.MINUTE,1);
break;
caseCustomDailyRollingFileAppender.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;
caseCustomDailyRollingFileAppender.HALF_DAY:
this.set(Calendar.MINUTE,0);
this.set(Calendar.SECOND,0);
this.set(Calendar.MILLISECOND,0);
inthour = 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;
caseCustomDailyRollingFileAppender.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;
caseCustomDailyRollingFileAppender.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;
caseCustomDailyRollingFileAppender.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:
thrownew IllegalStateException("Unknown periodicity type.");
}
returngetTime();
}
}
在resources目錄下,自定義log4j.xml日志配置文件:
<?xml version="1.0"encoding="UTF-8"?>
<!DOCTYPE log4j:configurationSYSTEM "log4j.dtd" >
<log4j:configuration>
<!-- 引用自定義的CustomDailyRollingFileAppender日志類-->
<appender name="FILE"class="com.demo.filter.CustomDailyRollingFileAppender">
<!-- 自定義日志輸出路徑和名稱 -->
<param name="file"value="D:/logs/test-agent.log" />
<!-- 自定義歷史日志文件后綴名 -->
<param name="datePattern"value="'.'yyyy-MM-dd" />
<!-- 自定義日志文件總數量,大于這個數量,自動刪除 -->
<param name="maxBackupIndex"value="3" />
<param name="append"value="true" />
<layoutclass="org.apache.log4j.PatternLayout">
<paramname="ConversionPattern" value="%d [%t] %p - %m%n" />
</layout>
</appender>
<root>
<priority value="info" />
<appender-ref ref="FILE"/>
</root>
</log4j:configuration>
main方法:
public static voidmain1(String[] args) {
finalFile file = new File("log4j.xml");
if(file.exists()) {
finalURL url = Main.class.getClassLoader()
.getResource("log4j.xml");
StringconfigFile = url.getPath();
PropertyConfigurator.configure("log4j.xml");
}
log.trace("Trace");
log.debug("Debug");
log.info("Info");
log.warn("Warn");
log.error("Error");
log.fatal("Fatal");
}
第一次啟動:
手動創建幾個其他日期的歷史文件:
第二次啟動:
log4j.rootLogger=INFO,demo
log4j.logger.org.mybatis = INFO
log4j.appender.demo=org.apache.log4j.ConsoleAppender
log4j.appender.demo.layout=org.apache.log4j.PatternLayout
log4j.appender.demo.layout.ConversionPattern=%m%n
log4j.appender.demo=com.demo.filter.CustomDailyRollingFileAppender
#log4j.appender.demo=org.apache.log4j.RollingFileAppender
log4j.appender.demo.File=D:/logs/demo.log
#log4j.appender.demo.Threshold=INFO
#log4j.appender.demo.ImmediateFlush=true
#log4j.appender.demo.Append=true
log4j.appender.demo.maxBackupIndex=3
log4j.appender.demo.layout.ConversionPattern=[%d{yyyy:MM:dd HH:mm:ss}]:%m \r\n
log4j.appender.demoConsole.encoding=utf-8
log4j.appender.demoConsole=org.apache.log4j.ConsoleAppender
log4j.appender.demoConsole.Target= System.out
log4j.appender.demoConsole.layout=org.apache.log4j.PatternLayout
log4j.appender.demoConsole.layout.ConversionPattern= [%d{yyyy:MM:dd HH:mm:ss}]:%m \r\n
Log4j由三個重要的組件構成:日志信息的優先級,日志信息的輸出目的地,日志信息的輸出格式。日志信息的優先級從高到低有ERROR、WARN、 INFO、DEBUG,分別用來指定這條日志信息的重要程度;日志信息的輸出目的地指定了日志將打印到控制臺還是文件中;而輸出格式則控制了日志信息的顯示內容。Log4j支持兩種配置文件格式,一種是XML格式的文件,一種是Java特性文件(鍵=值)。這里,我們主要探討基于XML的配置方式。
基本語法是:
其中,level 是日志記錄的優先級,分為OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或者您定義的級別。Log4j建議只使用四個級別,優先級從高到低分別是ERROR、WARN、INFO、DEBUG。
這里,每一個appenderName均是下面需要配置的日志信息的名稱。
實際例子:
log4j.rootLogger=INFO,stdout, ROLLING_ERROR_FILE, ROLLING_INFO_FILE
Log4j中提供的Appender主要有以下幾種:
org.apache.log4j.ConsoleAppender(控制臺),
org.apache.log4j.FileAppender(文件),
org.apache.log4j.DailyRollingFileAppender(每天產生一個日志文件),
org.apache.log4j.RollingFileAppender(文件大小到達指定尺寸的時候產生一個新的文件),
org.apache.log4j.WriterAppender(將日志信息以流格式發送到任意指定的地方)
基本語法為:
實際例子:
log4j.appender.ROLLING_ERROR_FILE=org.apache.log4j.DailyRollingFileAppender
針對不同的Appender,它們的屬性也有一定的差別,這些屬性主要是針對日志文件的命名規則、保存路徑、刪除策略等有關系。
Threshold=WARN:指定日志信息的最低輸出級別,默認為DEBUG。
ImmediateFlush=true:表示所有消息都會被立即輸出,設為false則不輸出,默認值是true。
Target=System.err:默認值是System.out。
Threshold=WARN:指定日志信息的最低輸出級別,默認為DEBUG。
ImmediateFlush=true:表示所有消息都會被立即輸出,設為false則不輸出,默認值是true。
Append=false:true表示消息增加到指定文件中,false則將消息覆蓋指定的文件內容,默認值是true。
File=D:/logs/logging.log4j:指定消息輸出到logging.log4j文件中。
Threshold=WARN:指定日志信息的最低輸出級別,默認為DEBUG。
ImmediateFlush=true:表示所有消息都會被立即輸出,設為false則不輸出,默認值是true。
Append=false:true表示消息增加到指定文件中,false則將消息覆蓋指定的文件內容,默認值是true。
File=D:/logs/logging.log4j:指定當前消息輸出到logging.log4j文件中。
DatePattern='.'yyyy-MM:每月滾動一次日志文件,即每月產生一個新的日志文件。當前月的日志文件名為logging.log4j,前一個月的日志文件名為logging.log4j.yyyy-MM。
另外,也可以指定按周、天、時、分等來滾動日志文件,對應的格式如下:
1)'.'yyyy-MM:每月
2)'.'yyyy-ww:每周
3)'.'yyyy-MM-dd:每天
4)'.'yyyy-MM-dd-a:每天兩次
5)'.'yyyy-MM-dd-HH:每小時
6)'.'yyyy-MM-dd-HH-mm:每分鐘
Threshold=WARN:指定日志信息的最低輸出級別,默認為DEBUG。
ImmediateFlush=true:表示所有消息都會被立即輸出,設為false則不輸出,默認值是true。
Append=false:true表示消息增加到指定文件中,false則將消息覆蓋指定的文件內容,默認值是true。
File=D:/logs/logging.log4j:指定消息輸出到logging.log4j文件中。
MaxFileSize=100KB:后綴可以是KB, MB 或者GB。在日志文件到達該大小時,將會自動滾動,即將原來的內容移到logging.log4j.1文件中。
MaxBackupIndex=2:指定可以產生的滾動文件的最大數,例如,設為2則可以產生logging.log4j.1,logging.log4j.2兩個滾動文件和一個logging.log4j文件。
Log4j提供的layout有以下幾種:
org.apache.log4j.HTMLLayout(以HTML表格形式布局),
org.apache.log4j.PatternLayout(可以靈活地指定布局模式),
org.apache.log4j.SimpleLayout(包含日志信息的級別和信息字符串),
org.apache.log4j.TTCCLayout(包含日志產生的時間、線程、類別等等信息)
Log4j采用類似C語言中的printf函數的打印格式格式化日志信息,打印參數如下:
%m 輸出代碼中指定的消息
%p 輸出優先級,即DEBUG,INFO,WARN,ERROR,FATAL
%r 輸出自應用啟動到輸出該log信息耗費的毫秒數
%c 輸出所屬的類目,通常就是所在類的全名
%t 輸出產生該日志事件的線程名
%n 輸出一個回車換行符,Windows平臺為“rn”,Unix平臺為“n”
%d 輸出日志時間點的日期或時間,默認格式為ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},輸出類似:2002年10月18日 22:10:28,921
%l 輸出日志事件的發生位置,包括類目名、發生的線程,以及在代碼中的行數。舉例:Testlog4.main(TestLog4.java:10)
基本語法為:
實際例子:
log4j.appender.ROLLING_ERROR_FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.ROLLING_ERROR_FILE.layout.ConversionPattern=[log]%d -%-4r [%t] %c %x%n %-5p - %m [%l] %n
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。