Correct.

If anybody wants it, here's a version that DOES rollover if appropriate when
the app is restarted.  I know it works 'cause we've been using it for months
in a production environment.

-Jim Moore


import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;

import org.apache.log4j.*;
import org.apache.log4j.spi.*;
import org.apache.log4j.helpers.*;


/**
 * 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/bar.log</code> and the <b>DatePattern</b> set to
 * <code>'.'yyyy-MM-dd</code>, on 2001-02-16 at midnight, the logging
 * file <code>/foo/bar.log</code> will be copied to
 * <code>/foo/bar.log.2001-02-16</code> and logging for 2001-02-17
 * will continue in <code>/foo/bar.log</code> until it is rolled over
 * itself the next day.
 * 
 * <p>Is is possible to specify monthly, weekly, half-daily, daily,
 * hourly, or minutely rollover schedules.
 * 
 * <p><table border="1">
 * <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>Assuming the first day of the week is Sunday, at Sunday 00:00,
 * March 25th, 2001, <code>/foo/bar.log</code> will be copied to
 * <code>/foo/bar.log.2001-03</code>. Logging for the month of April
 * will be output to <code>/foo/bar.log</code> until it is rolled over
 * itself at the beginning of May.
 * 
 * <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>At midnight, on March 31st, 2001, <code>/foo/bar.log</code>
 * will be copied to <code>/foo/bar.log.2001-08</code>. Logging for
 * the 9th week of 2001 will be output to <code>/foo/bar.log</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 9th, 2001, <code>/foo/bar.log</code> will
 * be copied to <code>/foo/bar.log.2001-03-08</code>. Logging for the
 * 9th day of March will be output to <code>/foo/bar.log</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, 2001, <code>/foo/bar.log</code> will be
 * copied to <code>/foo/bar.log.2001-03-09-AM</code>. Logging for the
 * afternoon of the 9th will be output to <code>/foo/bar.log</code>
 * until it is rolled over the next morning, i.e at midnight 00:00.
 * 
 * <tr>
 * <td><code>'.'yyyy-MM-dd-HH</code>
 * 
 * <td>Rollover at the top of every hour.</td>
 * 
 * <td>At approximately 11:00,000, on March 9th, 2001,
 * <code>/foo/bar.log</code> will be copied to
 * <code>/foo/bar.log.2001-03-09-10</code>. Logging for the 11th hour
 * of of the 9th of March will be output to <code>/foo/bar.log</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 minutue.</td>
 * 
 * <td>At approximately 11:23,000, on March 9th, 2001,
 * <code>/foo/bar.log</code> will be copied to
 * <code>/foo/bar.log.2001-03-09-10-22</code>. Logging for the minutue
 * of 11:23 (9th of March) will be output to
 * <code>/foo/bar.log</code> untill 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.
 * 
 * Ripped largely from DailyFileAppender in org.apache.log4j, but that
 *   wasn't written in an extensible way.
 * 
 * @author Jim Moore
 * @author Eirik Lygre
 * @author Ceki G&uuml;lc&uuml;
 */
public class DailyRollingFileAppender extends FileAppender {
  // The code assumes that the following constants are in a increasing
  // sequence.
  public static final int TOP_OF_TROUBLE = -1;
  public static final int TOP_OF_MINUTE = 0;
  public static final int TOP_OF_HOUR = 1;
  public static final int HALF_DAY = 2;
  public static final int TOP_OF_DAY = 3;
  public static final int TOP_OF_WEEK = 4;
  public static final int TOP_OF_MONTH = 5;

  /**
   * A string constant used in naming the option for setting the
   * filename pattern. Current value of this string constant is
   * <strong>DatePattern</strong>.
   */
  public static final String DATE_PATTERN_OPTION = "DatePattern";

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

  /**
   * The actual formatted filename that is currently being written to.
   */
  private String scheduledFilename;

  /**
   * The timestamp when we shall next recompute the filename.
   */
  private long nextCheck = System.currentTimeMillis() - 1;
  private Date now = new Date();
  private SimpleDateFormat sdf;
  private RollingCalendar rc = new RollingCalendar();
  private int checkPeriod = TOP_OF_TROUBLE;


  /**
   * 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);

    this.datePattern = 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;
  } 


  /**
   * Method declaration
   */
  public void activateOptions() {
    if (datePattern != null && fileName != null) {
      sdf = new SimpleDateFormat(datePattern);
  
      int type = computeCheckPeriod(datePattern);
  
      printPeriodicity(type);
      rc.setType(type);
  
      long currentTimeMillis = System.currentTimeMillis();
      
      String fileName = getFile();
      File file = new File(fileName);
  
      now.setTime(currentTimeMillis);
      nextCheck = rc.getNextCheckMillis(now);
      LogLog.debug("nextCheck = "+nextCheck);
      
      scheduledFilename = fileName + sdf.format(now);
      
      // determine if this needs to override the "append" setting
      if (getAppend()) {
        if ( currentTimeMillis >= rc.getNextCheckMillis(new
Date(file.lastModified())) ) {
          LogLog.debug(fileName+" is old or doesn't exist");
          setAppend(false);
          
          try {
            rollOver();
          }
          catch (IOException exp) {
            LogLog.error("Could not do a rollover", exp);
            return;
          }
        }
        else {
          LogLog.debug("the current log file is for today");
          setAppend(true);
        }
      }
  
      super.activateOptions();
    }
    else {
      LogLog.error("Either Filename or DatePattern options are not set for
[" 
                   + name + "].");
    } 
  } 


  /**
   * Method declaration
   *
   * @param type
   */
  protected 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 + "].");
    }
  } 


  /**
   * Method declaration
   *
   * @return
   */
  protected int computeCheckPeriod(String datePattern) {
    RollingCalendar c = new RollingCalendar();

    // set sate to 1970-01-01 00:00:00 GMT
    final Date epoch = new Date(0);

    if (datePattern != null) {
      for (int i = TOP_OF_MINUTE; i <= TOP_OF_MONTH; i++) {
        String r0 = sdf.format(epoch);

        c.setType(i);

        Date next = new Date(c.getNextCheckMillis(epoch));
        String r1 = sdf.format(next);

        LogLog.debug("Type = "+i+", r0 = "+r0+", r1 = "+r1);
        if ( r0 != null && r1 != null && !r0.equals(r1) ) {
          return i;
        }
      }
    }

    return TOP_OF_TROUBLE;  // Deliberately head for trouble...
  } 


  /**
   * Rollover the current file to a new file.
   */
  protected void rollOver() throws IOException {
    // Compute filename, but only if datePattern is specified
    if (datePattern == null) {
      errorHandler.error("Missing DatePattern option in rollOver().");
      return;
    }

    String fileName = getFile();
    File file = new File(fileName);

    String datedFilename = fileName + sdf.format(new
Date(file.lastModified()));

    // close current file, and rename it to datedFilename
    this.closeFile();

    File target = new File(datedFilename);

    if (target.exists()) {
      target.delete();
    }

    file.renameTo(target);
    LogLog.debug(fileName + " -> " + datedFilename);

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

    scheduledFilename = fileName + sdf.format(rc.getNextCheckDate(now));
  } 


  /**
   * This method differentiates DailyRollingFileAppender from its
   *   super class.
   */
  protected void subAppend(LoggingEvent event) {
    long n = System.currentTimeMillis();

    if (n >= nextCheck) {
      now.setTime(n);

      nextCheck = rc.getNextCheckMillis(now);

      try {
        rollOver();
      }
      catch (IOException ioe) {
        LogLog.error("rollOver() failed.", ioe);
      }
    }

    super.subAppend(event);
  }


  /**
   * RollingCalendar is a helper class to
   * DailyRollingFileAppender. Using this class, it is easy to compute
   * and access the next Millis().
   * 
   * It subclasses the standard {@link GregorianCalendar}-object, to
   * allow access to the protected function getTimeInMillis(), which it
   * then exports.
   * 
   * @author <a HREF="mailto:[EMAIL PROTECTED]";>Eirik Lygre</a>
   */
  protected static class RollingCalendar extends GregorianCalendar {
    int type = DailyRollingFileAppender.TOP_OF_TROUBLE;

    /**
     * Method declaration
     *
     * @param type
     */
    void setType(int type) {
      this.type = type;
    } 


    /**
     * Method declaration
     *
     * @param now
     *
     * @return
     */
    public long getNextCheckMillis(Date now) {
      return getNextCheckDate(now).getTime();
    } 


    /**
     * Method declaration
     *
     * @param now
     *
     * @return
     */
    public Date getNextCheckDate(Date now) {
      this.setTime(now);

      switch (type) {
        
        case DailyRollingFileAppender.TOP_OF_MINUTE:
          this.set(Calendar.SECOND, 0);
          this.set(Calendar.MILLISECOND, 0);
          this.add(Calendar.MINUTE, 1);

          break;

        case DailyRollingFileAppender.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 DailyRollingFileAppender.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, 0);
          }
          else {
            this.set(Calendar.HOUR_OF_DAY, 12);
          } 

          break;

        case DailyRollingFileAppender.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 DailyRollingFileAppender.TOP_OF_WEEK:
          this.set(Calendar.DAY_OF_WEEK, getFirstDayOfWeek());
          this.set(Calendar.HOUR_OF_DAY, 0);
          this.set(Calendar.SECOND, 0);
          this.set(Calendar.MILLISECOND, 0);
          this.add(Calendar.WEEK_OF_YEAR, 1);

          break;

        case DailyRollingFileAppender.TOP_OF_MONTH:
          this.set(Calendar.DATE, 1);
          this.set(Calendar.HOUR_OF_DAY, 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();
    } 

  }  // class RollingCalendar

}



-----Original Message-----
From: Amit Vaidya [mailto:[EMAIL PROTECTED]]
Sent: Wednesday, July 11, 2001 2:07 AM
To: LOG4J Users Mailing List
Cc: [EMAIL PROTECTED]
Subject: RE: a question about the DailyRollingFileAppender


Kevin,

What exactly do you mean by 'the application is running"?
I'm using the DailyRollingFileAppender through my standalone java
application.
So do you mean to say that there is no way that this RollingFileAppender
will 
work if my program is not running at the 'Rolling time instance'??

Regards,
Amit

-----Original Message-----
From: Kevin Steppe [mailto:[EMAIL PROTECTED]]
Sent: Tuesday, July 10, 2001 12:29 PM
To: LOG4J Users Mailing List
Subject: Re: a question about the DailyRollingFileAppender


Veronica,
    Under the current implementation the file is only copied to the new
name (dogtest.log -> dogtest.log.2001-07-10) if the application is
running at the time when the change should occur ie midnight.  So if you
stop the application before midnight and start it again the next day
then
it will continue to log to the same file.

    Is that the problem?

Kevin


$B=t(B wrote:

> Dear Mr.
>     I have a trouble about the setting of the datePattern option
> in DailyRollingFileAppender.
>     I wrote the setting as follows:
>          log4j.appender.D=org.apache.log4j.DailyRollingFileAppender
>          log4j.appender.D.File=dogtest.log
>          log4j.appender.D.DatePattern='.'yyyy-MM-dd
> the log contents are regularly output to the file "dogtest.log",
> but even the date changed, the file name won't be copied to
> "dogtest.log.2001-07-10".
>     Would you please tell me how to solve the problem.
>     Thank you in advance!
>                                                          Veronica Zhu
>                                                     [EMAIL PROTECTED]
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [EMAIL PROTECTED]
> For additional commands, e-mail: [EMAIL PROTECTED]


---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]


---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

application/ms-tnef

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to