import java.io.File;import java.io.IOException;import java.io.Writer;import java.text.SimpleDateFormat;import java.util.*;import org.apache.log4j.FileAppender;import org.apache.log4j.Layout;import org.apache.log4j.helpers.CountingQuietWriter;import org.apache.log4j.helpers.LogLog;import org.apache.log4j.helpers.OptionConverter;import org.apache.log4j.spi.LoggingEvent;/**
* ClassName: Log4JDateAndSizeSplit <br/>
* Function: Log4j生成日志类重写,该类可以按照日期+指定大小分隔日志. <br/>
* date: 2016年3月31日 下午12:44:14 <br/>
*
* @author lujie
*/publicclassLog4JDateAndSizeSplitextendsFileAppender{// The code assumes that the following constants are in a increasing// sequence.staticfinalint TOP_OF_TROUBLE =-1;staticfinalint TOP_OF_MINUTE =0;staticfinalint TOP_OF_HOUR =1;staticfinalint HALF_DAY =2;staticfinalint TOP_OF_DAY =3;staticfinalint TOP_OF_WEEK =4;staticfinalint TOP_OF_MONTH =5;/**
* The default maximum file size is 10MB.
*/protectedlong maxFileSize =10*1024*1024;/**
* There is one backup file by default.
*/protectedint maxBackupIndex =1;/**
* meaning daily rollover.
*/private String datePattern ="'.'yyyy-MM-dd";/**
* "scheduledFilename" at the beginning of the next hour.
*/private String scheduledFilename;/**
* The next time we estimate a rollover should occur.
*/privatelong nextCheck = System.currentTimeMillis()-1;
Date now =newDate();
SimpleDateFormat sdf;
RollingCalendar rc =newRollingCalendar();int checkPeriod = TOP_OF_TROUBLE;// The gmtTimeZone is used only in computeCheckPeriod() method.staticfinal TimeZone gmtTimeZone = TimeZone.getTimeZone("GMT");/**
* The default constructor does nothing.
*/publicLog4JDateAndSizeSplit(){}/**
* ouput destination for this appender.
*/publicLog4JDateAndSizeSplit(Layout layout, String filename, String datePattern)throws IOException {super(layout, filename,true);this.datePattern = datePattern;activateOptions();}/**
* being rolled over to backup files.
*
* @since 1.1
*/publiclonggetMaximumFileSize(){return maxFileSize;}/**
* being rolled over to backup files.
* <p>
* <p>
* JavaBeans {@link java.beans.Introspector Introspector}.
*
* @see #setMaxFileSize(String)
*/publicvoidsetMaximumFileSize(long maxFileSize){this.maxFileSize = maxFileSize;}/**
* being rolled over to backup files.
* <p>
* <p>
* the value "10KB" will be interpreted as 10240.
*/publicvoidsetMaxFileSize(String value){
maxFileSize = OptionConverter.toFileSize(value, maxFileSize +1);}/**
* Returns the value of the <b>MaxBackupIndex</b> option.
*/publicintgetMaxBackupIndex(){return maxBackupIndex;}/**
* Set the maximum number of backup files to keep around.
* <p>
* <p>
*/publicvoidsetMaxBackupIndex(int maxBackups){this.maxBackupIndex = maxBackups;}/**
*/publicvoidsetDatePattern(String pattern){
datePattern = pattern;}/**
* Returns the value of the <b>DatePattern</b> option.
*/public String getDatePattern(){return datePattern;}@OverridepublicvoidactivateOptions(){super.activateOptions();if(datePattern != null && fileName != null){
now.setTime(System.currentTimeMillis());
sdf =newSimpleDateFormat(datePattern);int type =computeCheckPeriod();printPeriodicity(type);
rc.setType(type);
File file =newFile(fileName);
scheduledFilename = fileName + sdf.format(newDate(file.lastModified()));}else{
LogLog.error("Either File or DatePattern options are not set for appender ["+ name +"].");}}voidprintPeriodicity(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).intcomputeCheckPeriod(){
RollingCalendar rollingCalendar =newRollingCalendar(gmtTimeZone, Locale.ENGLISH);// set sate to 1970-01-01 00:00:00 GMT
Date epoch =newDate(0);if(datePattern != null){for(int i = TOP_OF_MINUTE; i <= TOP_OF_MONTH; i++){
SimpleDateFormat simpleDateFormat =newSimpleDateFormat(datePattern);
simpleDateFormat.setTimeZone(gmtTimeZone);// do all date// formatting in GMT
String r0 = simpleDateFormat.format(epoch);
rollingCalendar.setType(i);
Date next =newDate(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...}/**
* Implements the usual roll over behaviour.
* <p>
* <p>
* If <code>MaxBackupIndex</code> is positive, then files {
* <p>
* <p>
* <p>
* If <code>MaxBackupIndex</code> is equal to zero, then the
* <code>File</code> is truncated with no backup files created.
*/public// synchronization not necessary since doAppend is alreasy synchedvoidsizeRollOver(){
File target;
File file;
LogLog.debug("rolling over count="+((CountingQuietWriter) qw).getCount());
LogLog.debug("maxBackupIndex="+ maxBackupIndex);
String datedFilename = fileName + sdf.format(now);if(maxBackupIndex >0){// Delete the oldest file, to keep Windows happy.
file =newFile(datedFilename +'.'+ maxBackupIndex);if(file.exists())
file.delete();// Map {(maxBackupIndex - 1), ..., 2, 1} to {maxBackupIndex, ..., 3,// 2}for(int i = maxBackupIndex -1; i >=1; i--){
file =newFile(datedFilename +"."+ i);if(file.exists()){
target =newFile(datedFilename +'.'+(i +1));
LogLog.debug("Renaming file "+ file +" to "+ target);
file.renameTo(target);}}// Rename fileName to datedFilename.1
target =newFile(datedFilename +"."+1);this.closeFile();// keep windows happy.
file =newFile(fileName);
LogLog.debug("Renaming file "+ file +" to "+ target);
file.renameTo(target);}elseif(maxBackupIndex <0){// infinite number of files// find the max backup indexfor(int i =1; i < Integer.MAX_VALUE; i++){
target =newFile(datedFilename +"."+ i);if(!target.exists()){// Rename fileName to datedFilename.ithis.closeFile();
file =newFile(fileName);
file.renameTo(target);
LogLog.debug("Renaming file "+ file +" to "+ target);break;}}}try{// This will also close the file. This is OK since multiple// close operations are safe.this.setFile(fileName,false, bufferedIO, bufferSize);}catch(IOException e){
LogLog.error("setFile("+ fileName +", false) call failed.", e);}
scheduledFilename = datedFilename;}@OverridepublicsynchronizedvoidsetFile(String fileName,boolean append,boolean bufferedIO,int bufferSize)throws IOException {super.setFile(fileName, append,this.bufferedIO,this.bufferSize);if(append){
File f =newFile(fileName);((CountingQuietWriter) qw).setCount(f.length());}}@OverrideprotectedvoidsetQWForFiles(Writer writer){this.qw =newCountingQuietWriter(writer, errorHandler);}/**
* Rollover the current file to a new file.
*/voidtimeRollOver()throws IOException {/* Compute filename, but only if datePattern is specified */if(datePattern == null){
errorHandler.error("Missing DatePattern option in rollOver().");return;}
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 datedFilenamethis.closeFile();
File target =newFile(scheduledFilename);if(target.exists()){
target.delete();}
File file =newFile(fileName);boolean result = file.renameTo(target);if(result){
LogLog.debug(fileName +" -> "+ scheduledFilename);}else{
LogLog.error("Failed to rename ["+ fileName +"] to ["+ scheduledFilename +"].");}try{// This will also close the file. This is OK since multiple// close operations are safe.super.setFile(fileName,false,this.bufferedIO,this.bufferSize);}catch(IOException e){
errorHandler.error("setFile("+ fileName +", false) call failed.");}
scheduledFilename = datedFilename;}/**
* <p>
* rollover.
*/@OverrideprotectedvoidsubAppend(LoggingEvent event){long n = System.currentTimeMillis();if(n >= nextCheck){
now.setTime(n);
nextCheck = rc.getNextCheckMillis(now);try{timeRollOver();}catch(IOException ioe){
LogLog.error("rollOver() failed.", ioe);}}elseif((fileName != null)&&((CountingQuietWriter) qw).getCount()>= maxFileSize){sizeRollOver();}super.subAppend(event);}classRollingCalendarextendsGregorianCalendar{privatestaticfinallong serialVersionUID =-3560331770601814177L;int type = Log4JDateAndSizeSplit.TOP_OF_TROUBLE;RollingCalendar(){super();}RollingCalendar(TimeZone tz, Locale locale){super(tz, locale);}voidsetType(int type){this.type = type;}publiclonggetNextCheckMillis(Date now){returngetNextCheckDate(now).getTime();}public Date getNextCheckDate(Date now){this.setTime(now);switch(type){case Log4JDateAndSizeSplit.TOP_OF_MINUTE:this.set(Calendar.SECOND,0);this.set(Calendar.MILLISECOND,0);this.add(Calendar.MINUTE,1);break;case Log4JDateAndSizeSplit.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 Log4JDateAndSizeSplit.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 Log4JDateAndSizeSplit.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 Log4JDateAndSizeSplit.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 Log4JDateAndSizeSplit.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:thrownewIllegalStateException("Unknown periodicity type.");}returngetTime();}}}