log4j之DailyRollingFileAppender源码 重写文件名后缀

时间:2025-03-22 22:02:07

DailyRollingFileAppende每天(yyyy-MM-dd)生成一个日志文件,但是当天的文件没有日期后缀。试着重写了一下他的方法。

主要注意这个方法public synchronized void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize)  throws IOException

还有这个void rollOver() throws IOException

【注】有个问题没有解决,当服务器将时间调向过去,日志不会写到那天的日志文件里,而是会写到最新的日志文件里;系统时间调向将来,没有问题。

/**
 * Copyright (C) The Apache Software Foundation. All rights reserved.
 *
 * This software is published under the terms of the Apache Software
 * License version 1.1, a copy of which has been included with this
 * distribution in the  file.  */



package ;

import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;

import ;
import .;
import .;
import .;

/***
   DailyRollingFileAppender extends {@link FileAppender} so that the
   underlying file is rolled over at a user chosen frequency.

   <p>The rolling schedule is specified by the <b>DatePattern</b>
   option. This pattern should follow the {@link SimpleDateFormat}
   conventions. In particular, you <em>must</em> escape literal text
   within a pair of single quotes. A formatted version of the date
   pattern is used as the suffix for the rolled file name.

   <p>For example, if the <b>File</b> option is set to
   <code>/foo/</code> and the <b>DatePattern</b> set to
   <code>'.'yyyy-MM-dd</code>, on 2001-02-16 at midnight, the logging
   file <code>/foo/</code> will be copied to
   <code>/foo/.2001-02-16</code> and logging for 2001-02-17
   will continue in <code>/foo/</code> until it rolls over
   the next day.

   <p>Is is possible to specify monthly, weekly, half-daily, daily,
   hourly, or minutely rollover schedules.

   <p><table border="1" cellpadding="2">
   <tr>
   <th>DatePattern</th>
   <th>Rollover schedule</th>
   <th>Example</th>

   <tr>
   <td><code>'.'yyyy-MM</code>
   <td>Rollover at the beginning of each month</td>

   <td>At midnight of May 31st, 2002 <code>/foo/</code> will be
   copied to <code>/foo/.2002-05</code>. Logging for the month
   of June will be output to <code>/foo/</code> until it is
   also rolled over the next month.

   <tr>
   <td><code>'.'yyyy-ww</code>

   <td>Rollover at the first day of each week. The first day of the
   week depends on the locale.</td>

   <td>Assuming the first day of the week is Sunday, on Saturday
   midnight, June 9th 2002, the file <i>/foo/</i> will be
   copied to <i>/foo/.2002-23</i>.  Logging for the 24th week
   of 2002 will be output to <code>/foo/</code> until it is
   rolled over the next week.

   <tr>
   <td><code>'.'yyyy-MM-dd</code>

   <td>Rollover at midnight each day.</td>

   <td>At midnight, on March 8th, 2002, <code>/foo/</code> will
   be copied to <code>/foo/.2002-03-08</code>. Logging for the
   9th day of March will be output to <code>/foo/</code> until
   it is rolled over the next day.

   <tr>
   <td><code>'.'yyyy-MM-dd-a</code>

   <td>Rollover at midnight and midday of each day.</td>

   <td>At noon, on March 9th, 2002, <code>/foo/</code> will be
   copied to <code>/foo/.2002-03-09-AM</code>. Logging for the
   afternoon of the 9th will be output to <code>/foo/</code>
   until it is rolled over at midnight.

   <tr>
   <td><code>'.'yyyy-MM-dd-HH</code>

   <td>Rollover at the top of every hour.</td>

   <td>At approximately 11:00.000 o'clock on March 9th, 2002,
   <code>/foo/</code> will be copied to
   <code>/foo/.2002-03-09-10</code>. Logging for the 11th hour
   of the 9th of March will be output to <code>/foo/</code>
   until it is rolled over at the beginning of the next hour.


   <tr>
   <td><code>'.'yyyy-MM-dd-HH-mm</code>

   <td>Rollover at the beginning of every minute.</td>

   <td>At approximately 11:23,000, on March 9th, 2001,
   <code>/foo/</code> will be copied to
   <code>/foo/.2001-03-09-10-22</code>. Logging for the minute
   of 11:23 (9th of March) will be output to
   <code>/foo/</code> until it is rolled over the next minute.

   </table>

   <p>Do not use the colon ":" character in anywhere in the
   <b>DatePattern</b> option. The text before the colon is interpeted
   as the protocol specificaion of a URL which is probably not what
   you want.


   @author Eirik Lygre
   @author Ceki Gülcü */
public class DailyRollingFileAppender extends FileAppender {


  // The code assumes that the following constants are in a increasing
  // sequence.
  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;


  /***
     The date pattern. By default, the pattern is set to
     "'.'yyyy-MM-dd" meaning daily rollover.
   */
  private String datePattern = "'.'yyyy-MM-dd";

  /***
     The log file will be renamed to the value of the
     scheduledFilename variable when the next interval is entered. For
     example, if the rollover period is one hour, the log file will be
     renamed to the value of "scheduledFilename" at the beginning of
     the next hour. 

     The precise time when a rollover occurs depends on logging
     activity. 
  */
  private String scheduledFilename;

  /***
     The next time we estimate a rollover should occur. */
  private long nextCheck =  () - 1;

  Date now = new Date();

  SimpleDateFormat sdf;

  RollingCalendar rc = new RollingCalendar();

  int checkPeriod = TOP_OF_TROUBLE;

  // The gmtTimeZone is used only in computeCheckPeriod() method.
  static final TimeZone gmtTimeZone = ("GMT");


  /***
     The default constructor does nothing. */
  public DailyRollingFileAppender() {
  }

  /***
    Instantiate a <code>DailyRollingFileAppender</code> and open the
    file designated by <code>filename</code>. The opened filename will
    become the ouput destination for this appender.

    */
  public DailyRollingFileAppender (Layout layout, String filename,
				   String datePattern) throws IOException {
    super(layout, filename, true);
     = datePattern;
    activateOptions();
  }

  /***
     The <b>DatePattern</b> takes a string in the same format as
     expected by {@link SimpleDateFormat}. This options determines the
     rollover schedule.
   */
  public void setDatePattern(String pattern) {
    datePattern = pattern;
  }

  /*** Returns the value of the <b>DatePattern</b> option. */
  public String getDatePattern() {
    return datePattern;
  }

  public void activateOptions() {
    ();
    if(datePattern != null && fileName != null) {
      (());
      sdf = new SimpleDateFormat(datePattern);
      int type = computeCheckPeriod();
      printPeriodicity(type);
      (type);
      File file = new File(fileName);
//      String name0=(new Date());
//      String name1=((new Date()));
//      if((name0)||(name1)){
    	  scheduledFilename = fileName;
//      }else{
//    	  scheduledFilename = fileName+(new Date(()));
//      }
    	  
    } else {
      ("Either File or DatePattern options are not set for appender ["
		   +name+"].");
    }
  }

  void printPeriodicity(int type) {
    switch(type) {
    case TOP_OF_MINUTE:
      ("Appender ["+name+"] to be rolled every minute.");
      break;
    case TOP_OF_HOUR:
      ("Appender ["+name
		   +"] to be rolled on top of every hour.");
      break;
    case HALF_DAY:
      ("Appender ["+name
		   +"] to be rolled at midday and midnight.");
      break;
    case TOP_OF_DAY:
      ("Appender ["+name
		   +"] to be rolled at midnight.");
      break;
    case TOP_OF_WEEK:
      ("Appender ["+name
		   +"] to be rolled at start of week.");
      break;
    case TOP_OF_MONTH:
      ("Appender ["+name
		   +"] to be rolled at start of every month.");
      break;
    default:
      ("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, );
    // set sate to 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);
	(gmtTimeZone); // do all date formatting in GMT
	String r0 = (epoch);
	(i);
	Date next = new Date((epoch));
	String r1 =  (next);
	//("Type = "+i+", r0 = "+r0+", r1 = "+r1);
	if(r0 != null && r1 != null && !(r1)) {
	  return i;
	}
      }
    }
    return TOP_OF_TROUBLE; // Deliberately head for trouble...
  }

  /***
     Rollover the current file to a new file.
  */
  void rollOver() throws IOException {

    /** Compute filename, but only if datePattern is specified */
    if (datePattern == null) {
      ("Missing DatePattern option in rollOver().");
      return;
    }
    
    
    String datedFilename = fileName;
    String str=(now);
    =(0, ("."))+str;
    // 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 ((datedFilename)) {
      return;
    }

    // close current file, and rename it to datedFilename
    ();
    
    File target  = new File(scheduledFilename);
    if (()) {
      ();
      String s=null;
    }
//注意这里!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//    File file = new File(fileName);
//    boolean result = (target);
//    if(result) {
//      (fileName +" -> "+ scheduledFilename);
//    } else {
//      ("Failed to rename ["+fileName+"] to ["+scheduledFilename+"].");
//    }

    try {
      // This will also close the file. This is OK since multiple
      // close operations are safe.
      (fileName, false, , );
    }
    catch(IOException e) {
      ("setFile("+fileName+", false) call failed.");
    }
    scheduledFilename = datedFilename;
  }

  /***
   * This method differentiates DailyRollingFileAppender from its
   * super class.
   *
   * <p>Before actually logging, this method will check whether it is
   * time to do a rollover. If it is, it will schedule the next
   * rollover time and then rollover.
   * */
  protected void subAppend(LoggingEvent event) {
    long n = ();
    if (n >= nextCheck) {
      (n);
      nextCheck = (now);
      try {
	rollOver();
      }
      catch(IOException ioe) {
	("rollOver() failed.", ioe);
      }
    }
    (event);
   }
}

/***
 *  RollingCalendar is a helper class to DailyRollingFileAppender.
 *  Given a periodicity type and the current time, it computes the
 *  start of the next interval.  
 * */
class RollingCalendar extends GregorianCalendar {

  int type = DailyRollingFileAppender.TOP_OF_TROUBLE;

  RollingCalendar() {
    super();
  }  

  RollingCalendar(TimeZone tz, Locale locale) {
    super(tz, locale);
  }  

  void setType(int type) {
     = type;
  }

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

  public Date getNextCheckDate(Date now) {
    (now);

    switch(type) {
    case DailyRollingFileAppender.TOP_OF_MINUTE:
	(, 0);
	(, 0);
	(, 1);
	break;
    case DailyRollingFileAppender.TOP_OF_HOUR:
	(, 0);
	(, 0);
	(, 0);
	(Calendar.HOUR_OF_DAY, 1);
	break;
    case DailyRollingFileAppender.HALF_DAY:
	(, 0);
	(, 0);
	(, 0);
	int hour = get(Calendar.HOUR_OF_DAY);
	if(hour < 12) {
	  (Calendar.HOUR_OF_DAY, 12);
	} else {
	  (Calendar.HOUR_OF_DAY, 0);
	  (Calendar.DAY_OF_MONTH, 1);
	}
	break;
    case DailyRollingFileAppender.TOP_OF_DAY:
	(Calendar.HOUR_OF_DAY, 0);
	(, 0);
	(, 0);
	(, 0);
	(, 1);
	break;
    case DailyRollingFileAppender.TOP_OF_WEEK:
	(Calendar.DAY_OF_WEEK, getFirstDayOfWeek());
	(Calendar.HOUR_OF_DAY, 0);
	(, 0);
	(, 0);
	(Calendar.WEEK_OF_YEAR, 1);
	break;
    case DailyRollingFileAppender.TOP_OF_MONTH:
	(, 1);
	(Calendar.HOUR_OF_DAY, 0);
	(, 0);
	(, 0);
	(, 1);
	break;
    default:
	throw new IllegalStateException("Unknown periodicity type.");
    }
    return getTime();
  }
}
/**
 * Copyright (C) The Apache Software Foundation. All rights reserved.
 *
 * This software is published under the terms of the Apache Software
 * License version 1.1, a copy of which has been included with this
 * distribution in the  file.  */

package ;

import ;
import ;
import ;
import ;
import ;

import .;
import .;
import .;
import .;
import .;
import .;
import .;

// Contibutors: Jens Uwe Pipka <@>
//              Ben Sandee

/***
 *  FileAppender appends log events to a file.
 *
 *  <p>Support for <code></code> and console appending
 *  has been deprecated and then removed. See the replacement
 *  solutions: {@link WriterAppender} and {@link ConsoleAppender}.
 *
 * @author Ceki Gülcü 
 * */
public class FileAppender extends WriterAppender {

  /*** Append to or truncate the file? The default value for this
      variable is <code>true</code>, meaning that by default a
      <code>FileAppender</code> will append to an existing file and
      not truncate it.

      <p>This option is meaningful only if the FileAppender opens the
      file.
  */
  protected boolean fileAppend = true;

  /***
     The name of the log file. */
  protected String fileName = null;

  /***
     Do we do bufferedIO? */
  protected boolean bufferedIO = false;

  /***
     How big should the IO buffer be? Default is 8K. */
  protected int bufferSize = 8*1024;

  protected static  simpleDateFormat = new ("yyyy-MM-dd");

  
  /***
     The default constructor does not do anything.
  */
  public
  FileAppender() {
  }

  /***
    Instantiate a <code>FileAppender</code> and open the file
    designated by <code>filename</code>. The opened filename will
    become the output destination for this appender.

    <p>If the <code>append</code> parameter is true, the file will be
    appended to. Otherwise, the file designated by
    <code>filename</code> will be truncated before being opened.

    <p>If the <code>bufferedIO</code> parameter is <code>true</code>,
    then buffered IO will be used to write to the output file.

  */
  public
  FileAppender(Layout layout, String filename, boolean append, boolean bufferedIO,
	       int bufferSize) throws IOException {
     = layout;
    filename=filename+"."+(new Date());
    (filename, append, bufferedIO, bufferSize);
  }

  /***
    Instantiate a FileAppender and open the file designated by
    <code>filename</code>. The opened filename will become the output
    destination for this appender.

    <p>If the <code>append</code> parameter is true, the file will be
    appended to. Otherwise, the file designated by
    <code>filename</code> will be truncated before being opened.
  */
  public
  FileAppender(Layout layout, String filename, boolean append)
                                                             throws IOException {
	 = layout;
    (filename, append, false, bufferSize);
  }

  /***
     Instantiate a FileAppender and open the file designated by
    <code>filename</code>. The opened filename will become the output
    destination for this appender.

    <p>The file will be appended to.  */
  public
  FileAppender(Layout layout, String filename) throws IOException {
    this(layout, filename, true);
  }

  /***
     The <b>File</b> property takes a string value which should be the
     name of the file to append to.

     <p><font color="#DD0044"><b>Note that the special values
     "" or "" are no longer honored.</b></font>

     <p>Note: Actual opening of the file is made when {@link
     #activateOptions} is called, not when the options are set.  */
  public void setFile(String file) {
    // Trim spaces from both ends. The users probably does not want
    // trailing spaces in file names.
    String val = ();
    fileName = val;
  }

  /***
      Returns the value of the <b>Append</b> option.
   */
  public
  boolean getAppend() {
    return fileAppend;
  }


  /*** Returns the value of the <b>File</b> option. */
  public
  String getFile() {
    return fileName;
  }

  /***
     If the value of <b>File</b> is not <code>null</code>, then {@link
     #setFile} is called with the values of <b>File</b>  and
     <b>Append</b> properties.

     @since 0.8.1 */
  public
  void activateOptions() {
    if(fileName != null) {
      try {
	setFile(fileName, fileAppend, bufferedIO, bufferSize);
      }
      catch( e) {
	("setFile("+fileName+","+fileAppend+") call failed.",
			   e, ErrorCode.FILE_OPEN_FAILURE);
      }
    } else {
      //("File option not set for appender ["+name+"].");
      ("File option not set for appender ["+name+"].");
      ("Are you using FileAppender instead of ConsoleAppender?");
    }
  }

 /***
     Closes the previously opened file.
  */
  protected
  void closeFile() {
    if( != null) {
      try {
	();
      }
      catch( e) {
	// Exceptionally, it does not make sense to delegate to an
	// ErrorHandler. Since a closed appender is basically dead.
	("Could not close " + qw, e);
      }
    }
  }

  /***
     Get the value of the <b>BufferedIO</b> option.

     <p>BufferedIO will significatnly increase performance on heavily
     loaded systems.

  */
  public
  boolean getBufferedIO() {
    return ;
  }


  /***
     Get the size of the IO buffer.
  */
  public
  int getBufferSize() {
    return ;
  }



  /***
     The <b>Append</b> option takes a boolean value. It is set to
     <code>true</code> by default. If true, then <code>File</code>
     will be opened in append mode by {@link #setFile setFile} (see
     above). Otherwise, {@link #setFile setFile} will open
     <code>File</code> in truncate mode.

     <p>Note: Actual opening of the file is made when {@link
     #activateOptions} is called, not when the options are set.
   */
  public
  void setAppend(boolean flag) {
    fileAppend = flag;
  }

  /***
     The <b>BufferedIO</b> option takes a boolean value. It is set to
     <code>false</code> by default. If true, then <code>File</code>
     will be opened and the resulting {@link } wrapped
     around a {@link BufferedWriter}.

     BufferedIO will significatnly increase performance on heavily
     loaded systems.

  */
  public
  void setBufferedIO(boolean bufferedIO) {
     = bufferedIO;
    if(bufferedIO) {
      immediateFlush = false;
    }
  }


  /***
     Set the size of the IO buffer.
  */
  public
  void setBufferSize(int bufferSize) {
     = bufferSize;
  }

  /***
    <p>Sets and <i>opens</i> the file where the log output will
    go. The specified file must be writable.

    <p>If there was already an opened file, then the previous file
    is closed first.

    <p><b>Do not use this method directly. To configure a FileAppender
    or one of its subclasses, set its properties one by one and then
    call activateOptions.</b>

    @param fileName The path to the log file.
    @param append   If true will append to fileName. Otherwise will
        truncate fileName.  */
  public
  synchronized
  void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize)
                                                            throws IOException {
    ("setFile called: "+fileName+", "+append);

    // It does not make sense to have immediate flush and bufferedIO.
    if(bufferedIO) {
      setImmediateFlush(false);
    }
//注意这里。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
    reset();
    String suffix=(new Date());
    if((".")){
	    String str=(0, ("."));
	    if((".")){
	    	=(0, ("."))+"."+suffix;
	    	fileName=(0, ("."))+"."+suffix;
	    }
	    else{
	    	=str+"."+suffix;
	    	fileName=str+"."+suffix;
	    }
    }else{
    	=fileName+"."+suffix;
    	fileName=fileName+"."+suffix;
    	
    }
    Writer fw = createWriter(new FileOutputStream(fileName, append));
    if(bufferedIO) {
      fw = new BufferedWriter(fw, bufferSize);
    }
    (fw);
     = fileName;
     = append;
     = bufferedIO;
     = bufferSize;
    writeHeader();
    ("setFile ended");
  }


  /***
     Sets the quiet writer being used.

     This method is overriden by {@link RollingFileAppender}.
   */
  protected
  void setQWForFiles(Writer writer) {
      = new QuietWriter(writer, errorHandler);
  }


  /***
     Close any previously opened file and call the parent's
     <code>reset</code>.  */
  protected
  void reset() {
    closeFile();
     = null;
    ();
  }
}