Home » apache-log4j-1.2.15 » org.apache » log4j » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    * 
    9    *      http://www.apache.org/licenses/LICENSE-2.0
   10    * 
   11    * Unless required by applicable law or agreed to in writing, software
   12    * distributed under the License is distributed on an "AS IS" BASIS,
   13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    * See the License for the specific language governing permissions and
   15    * limitations under the License.
   16    */
   17   
   18   
   19   
   20   package org.apache.log4j;
   21   
   22   import java.io.IOException;
   23   import java.io.File;
   24   import java.text.SimpleDateFormat;
   25   import java.util.Date;
   26   import java.util.GregorianCalendar;
   27   import java.util.Calendar;
   28   import java.util.TimeZone;
   29   import java.util.Locale;
   30   
   31   import org.apache.log4j.helpers.LogLog;
   32   import org.apache.log4j.spi.LoggingEvent;
   33   
   34   /**
   35      DailyRollingFileAppender extends {@link FileAppender} so that the
   36      underlying file is rolled over at a user chosen frequency.
   37   
   38      <p>The rolling schedule is specified by the <b>DatePattern</b>
   39      option. This pattern should follow the {@link SimpleDateFormat}
   40      conventions. In particular, you <em>must</em> escape literal text
   41      within a pair of single quotes. A formatted version of the date
   42      pattern is used as the suffix for the rolled file name.
   43   
   44      <p>For example, if the <b>File</b> option is set to
   45      <code>/foo/bar.log</code> and the <b>DatePattern</b> set to
   46      <code>'.'yyyy-MM-dd</code>, on 2001-02-16 at midnight, the logging
   47      file <code>/foo/bar.log</code> will be copied to
   48      <code>/foo/bar.log.2001-02-16</code> and logging for 2001-02-17
   49      will continue in <code>/foo/bar.log</code> until it rolls over
   50      the next day.
   51   
   52      <p>Is is possible to specify monthly, weekly, half-daily, daily,
   53      hourly, or minutely rollover schedules.
   54   
   55      <p><table border="1" cellpadding="2">
   56      <tr>
   57      <th>DatePattern</th>
   58      <th>Rollover schedule</th>
   59      <th>Example</th>
   60   
   61      <tr>
   62      <td><code>'.'yyyy-MM</code>
   63      <td>Rollover at the beginning of each month</td>
   64   
   65      <td>At midnight of May 31st, 2002 <code>/foo/bar.log</code> will be
   66      copied to <code>/foo/bar.log.2002-05</code>. Logging for the month
   67      of June will be output to <code>/foo/bar.log</code> until it is
   68      also rolled over the next month.
   69   
   70      <tr>
   71      <td><code>'.'yyyy-ww</code>
   72   
   73      <td>Rollover at the first day of each week. The first day of the
   74      week depends on the locale.</td>
   75   
   76      <td>Assuming the first day of the week is Sunday, on Saturday
   77      midnight, June 9th 2002, the file <i>/foo/bar.log</i> will be
   78      copied to <i>/foo/bar.log.2002-23</i>.  Logging for the 24th week
   79      of 2002 will be output to <code>/foo/bar.log</code> until it is
   80      rolled over the next week.
   81   
   82      <tr>
   83      <td><code>'.'yyyy-MM-dd</code>
   84   
   85      <td>Rollover at midnight each day.</td>
   86   
   87      <td>At midnight, on March 8th, 2002, <code>/foo/bar.log</code> will
   88      be copied to <code>/foo/bar.log.2002-03-08</code>. Logging for the
   89      9th day of March will be output to <code>/foo/bar.log</code> until
   90      it is rolled over the next day.
   91   
   92      <tr>
   93      <td><code>'.'yyyy-MM-dd-a</code>
   94   
   95      <td>Rollover at midnight and midday of each day.</td>
   96   
   97      <td>At noon, on March 9th, 2002, <code>/foo/bar.log</code> will be
   98      copied to <code>/foo/bar.log.2002-03-09-AM</code>. Logging for the
   99      afternoon of the 9th will be output to <code>/foo/bar.log</code>
  100      until it is rolled over at midnight.
  101   
  102      <tr>
  103      <td><code>'.'yyyy-MM-dd-HH</code>
  104   
  105      <td>Rollover at the top of every hour.</td>
  106   
  107      <td>At approximately 11:00.000 o'clock on March 9th, 2002,
  108      <code>/foo/bar.log</code> will be copied to
  109      <code>/foo/bar.log.2002-03-09-10</code>. Logging for the 11th hour
  110      of the 9th of March will be output to <code>/foo/bar.log</code>
  111      until it is rolled over at the beginning of the next hour.
  112   
  113   
  114      <tr>
  115      <td><code>'.'yyyy-MM-dd-HH-mm</code>
  116   
  117      <td>Rollover at the beginning of every minute.</td>
  118   
  119      <td>At approximately 11:23,000, on March 9th, 2001,
  120      <code>/foo/bar.log</code> will be copied to
  121      <code>/foo/bar.log.2001-03-09-10-22</code>. Logging for the minute
  122      of 11:23 (9th of March) will be output to
  123      <code>/foo/bar.log</code> until it is rolled over the next minute.
  124   
  125      </table>
  126   
  127      <p>Do not use the colon ":" character in anywhere in the
  128      <b>DatePattern</b> option. The text before the colon is interpeted
  129      as the protocol specificaion of a URL which is probably not what
  130      you want.
  131   
  132   
  133      @author Eirik Lygre
  134      @author Ceki G&uuml;lc&uuml; */
  135   public class DailyRollingFileAppender extends FileAppender {
  136   
  137   
  138     // The code assumes that the following constants are in a increasing
  139     // sequence.
  140     static final int TOP_OF_TROUBLE=-1;
  141     static final int TOP_OF_MINUTE = 0;
  142     static final int TOP_OF_HOUR   = 1;
  143     static final int HALF_DAY      = 2;
  144     static final int TOP_OF_DAY    = 3;
  145     static final int TOP_OF_WEEK   = 4;
  146     static final int TOP_OF_MONTH  = 5;
  147   
  148   
  149     /**
  150        The date pattern. By default, the pattern is set to
  151        "'.'yyyy-MM-dd" meaning daily rollover.
  152      */
  153     private String datePattern = "'.'yyyy-MM-dd";
  154   
  155     /**
  156        The log file will be renamed to the value of the
  157        scheduledFilename variable when the next interval is entered. For
  158        example, if the rollover period is one hour, the log file will be
  159        renamed to the value of "scheduledFilename" at the beginning of
  160        the next hour. 
  161   
  162        The precise time when a rollover occurs depends on logging
  163        activity. 
  164     */
  165     private String scheduledFilename;
  166   
  167     /**
  168        The next time we estimate a rollover should occur. */
  169     private long nextCheck = System.currentTimeMillis () - 1;
  170   
  171     Date now = new Date();
  172   
  173     SimpleDateFormat sdf;
  174   
  175     RollingCalendar rc = new RollingCalendar();
  176   
  177     int checkPeriod = TOP_OF_TROUBLE;
  178   
  179     // The gmtTimeZone is used only in computeCheckPeriod() method.
  180     static final TimeZone gmtTimeZone = TimeZone.getTimeZone("GMT");
  181   
  182   
  183     /**
  184        The default constructor does nothing. */
  185     public DailyRollingFileAppender() {
  186     }
  187   
  188     /**
  189       Instantiate a <code>DailyRollingFileAppender</code> and open the
  190       file designated by <code>filename</code>. The opened filename will
  191       become the ouput destination for this appender.
  192   
  193       */
  194     public DailyRollingFileAppender (Layout layout, String filename,
  195   				   String datePattern) throws IOException {
  196       super(layout, filename, true);
  197       this.datePattern = datePattern;
  198       activateOptions();
  199     }
  200   
  201     /**
  202        The <b>DatePattern</b> takes a string in the same format as
  203        expected by {@link SimpleDateFormat}. This options determines the
  204        rollover schedule.
  205      */
  206     public void setDatePattern(String pattern) {
  207       datePattern = pattern;
  208     }
  209   
  210     /** Returns the value of the <b>DatePattern</b> option. */
  211     public String getDatePattern() {
  212       return datePattern;
  213     }
  214   
  215     public void activateOptions() {
  216       super.activateOptions();
  217       if(datePattern != null && fileName != null) {
  218         now.setTime(System.currentTimeMillis());
  219         sdf = new SimpleDateFormat(datePattern);
  220         int type = computeCheckPeriod();
  221         printPeriodicity(type);
  222         rc.setType(type);
  223         File file = new File(fileName);
  224         scheduledFilename = fileName+sdf.format(new Date(file.lastModified()));
  225   
  226       } else {
  227         LogLog.error("Either File or DatePattern options are not set for appender ["
  228   		   +name+"].");
  229       }
  230     }
  231   
  232     void printPeriodicity(int type) {
  233       switch(type) {
  234       case TOP_OF_MINUTE:
  235         LogLog.debug("Appender ["+name+"] to be rolled every minute.");
  236         break;
  237       case TOP_OF_HOUR:
  238         LogLog.debug("Appender ["+name
  239   		   +"] to be rolled on top of every hour.");
  240         break;
  241       case HALF_DAY:
  242         LogLog.debug("Appender ["+name
  243   		   +"] to be rolled at midday and midnight.");
  244         break;
  245       case TOP_OF_DAY:
  246         LogLog.debug("Appender ["+name
  247   		   +"] to be rolled at midnight.");
  248         break;
  249       case TOP_OF_WEEK:
  250         LogLog.debug("Appender ["+name
  251   		   +"] to be rolled at start of week.");
  252         break;
  253       case TOP_OF_MONTH:
  254         LogLog.debug("Appender ["+name
  255   		   +"] to be rolled at start of every month.");
  256         break;
  257       default:
  258         LogLog.warn("Unknown periodicity for appender ["+name+"].");
  259       }
  260     }
  261   
  262   
  263     // This method computes the roll over period by looping over the
  264     // periods, starting with the shortest, and stopping when the r0 is
  265     // different from from r1, where r0 is the epoch formatted according
  266     // the datePattern (supplied by the user) and r1 is the
  267     // epoch+nextMillis(i) formatted according to datePattern. All date
  268     // formatting is done in GMT and not local format because the test
  269     // logic is based on comparisons relative to 1970-01-01 00:00:00
  270     // GMT (the epoch).
  271   
  272     int computeCheckPeriod() {
  273       RollingCalendar rollingCalendar = new RollingCalendar(gmtTimeZone, Locale.ENGLISH);
  274       // set sate to 1970-01-01 00:00:00 GMT
  275       Date epoch = new Date(0);
  276       if(datePattern != null) {
  277         for(int i = TOP_OF_MINUTE; i <= TOP_OF_MONTH; i++) {
  278   	SimpleDateFormat simpleDateFormat = new SimpleDateFormat(datePattern);
  279   	simpleDateFormat.setTimeZone(gmtTimeZone); // do all date formatting in GMT
  280   	String r0 = simpleDateFormat.format(epoch);
  281   	rollingCalendar.setType(i);
  282   	Date next = new Date(rollingCalendar.getNextCheckMillis(epoch));
  283   	String r1 =  simpleDateFormat.format(next);
  284   	//System.out.println("Type = "+i+", r0 = "+r0+", r1 = "+r1);
  285   	if(r0 != null && r1 != null && !r0.equals(r1)) {
  286   	  return i;
  287   	}
  288         }
  289       }
  290       return TOP_OF_TROUBLE; // Deliberately head for trouble...
  291     }
  292   
  293     /**
  294        Rollover the current file to a new file.
  295     */
  296     void rollOver() throws IOException {
  297   
  298       /* Compute filename, but only if datePattern is specified */
  299       if (datePattern == null) {
  300         errorHandler.error("Missing DatePattern option in rollOver().");
  301         return;
  302       }
  303   
  304       String datedFilename = fileName+sdf.format(now);
  305       // It is too early to roll over because we are still within the
  306       // bounds of the current interval. Rollover will occur once the
  307       // next interval is reached.
  308       if (scheduledFilename.equals(datedFilename)) {
  309         return;
  310       }
  311   
  312       // close current file, and rename it to datedFilename
  313       this.closeFile();
  314   
  315       File target  = new File(scheduledFilename);
  316       if (target.exists()) {
  317         target.delete();
  318       }
  319   
  320       File file = new File(fileName);
  321       boolean result = file.renameTo(target);
  322       if(result) {
  323         LogLog.debug(fileName +" -> "+ scheduledFilename);
  324       } else {
  325         LogLog.error("Failed to rename ["+fileName+"] to ["+scheduledFilename+"].");
  326       }
  327   
  328       try {
  329         // This will also close the file. This is OK since multiple
  330         // close operations are safe.
  331         this.setFile(fileName, false, this.bufferedIO, this.bufferSize);
  332       }
  333       catch(IOException e) {
  334         errorHandler.error("setFile("+fileName+", false) call failed.");
  335       }
  336       scheduledFilename = datedFilename;
  337     }
  338   
  339     /**
  340      * This method differentiates DailyRollingFileAppender from its
  341      * super class.
  342      *
  343      * <p>Before actually logging, this method will check whether it is
  344      * time to do a rollover. If it is, it will schedule the next
  345      * rollover time and then rollover.
  346      * */
  347     protected void subAppend(LoggingEvent event) {
  348       long n = System.currentTimeMillis();
  349       if (n >= nextCheck) {
  350         now.setTime(n);
  351         nextCheck = rc.getNextCheckMillis(now);
  352         try {
  353   	rollOver();
  354         }
  355         catch(IOException ioe) {
  356   	LogLog.error("rollOver() failed.", ioe);
  357         }
  358       }
  359       super.subAppend(event);
  360      }
  361   }
  362   
  363   /**
  364    *  RollingCalendar is a helper class to DailyRollingFileAppender.
  365    *  Given a periodicity type and the current time, it computes the
  366    *  start of the next interval.  
  367    * */
  368   class RollingCalendar extends GregorianCalendar {
  369     private static final long serialVersionUID = -3560331770601814177L;
  370   
  371     int type = DailyRollingFileAppender.TOP_OF_TROUBLE;
  372   
  373     RollingCalendar() {
  374       super();
  375     }  
  376   
  377     RollingCalendar(TimeZone tz, Locale locale) {
  378       super(tz, locale);
  379     }  
  380   
  381     void setType(int type) {
  382       this.type = type;
  383     }
  384   
  385     public long getNextCheckMillis(Date now) {
  386       return getNextCheckDate(now).getTime();
  387     }
  388   
  389     public Date getNextCheckDate(Date now) {
  390       this.setTime(now);
  391   
  392       switch(type) {
  393       case DailyRollingFileAppender.TOP_OF_MINUTE:
  394   	this.set(Calendar.SECOND, 0);
  395   	this.set(Calendar.MILLISECOND, 0);
  396   	this.add(Calendar.MINUTE, 1);
  397   	break;
  398       case DailyRollingFileAppender.TOP_OF_HOUR:
  399   	this.set(Calendar.MINUTE, 0);
  400   	this.set(Calendar.SECOND, 0);
  401   	this.set(Calendar.MILLISECOND, 0);
  402   	this.add(Calendar.HOUR_OF_DAY, 1);
  403   	break;
  404       case DailyRollingFileAppender.HALF_DAY:
  405   	this.set(Calendar.MINUTE, 0);
  406   	this.set(Calendar.SECOND, 0);
  407   	this.set(Calendar.MILLISECOND, 0);
  408   	int hour = get(Calendar.HOUR_OF_DAY);
  409   	if(hour < 12) {
  410   	  this.set(Calendar.HOUR_OF_DAY, 12);
  411   	} else {
  412   	  this.set(Calendar.HOUR_OF_DAY, 0);
  413   	  this.add(Calendar.DAY_OF_MONTH, 1);
  414   	}
  415   	break;
  416       case DailyRollingFileAppender.TOP_OF_DAY:
  417   	this.set(Calendar.HOUR_OF_DAY, 0);
  418   	this.set(Calendar.MINUTE, 0);
  419   	this.set(Calendar.SECOND, 0);
  420   	this.set(Calendar.MILLISECOND, 0);
  421   	this.add(Calendar.DATE, 1);
  422   	break;
  423       case DailyRollingFileAppender.TOP_OF_WEEK:
  424   	this.set(Calendar.DAY_OF_WEEK, getFirstDayOfWeek());
  425   	this.set(Calendar.HOUR_OF_DAY, 0);
  426   	this.set(Calendar.MINUTE, 0);
  427   	this.set(Calendar.SECOND, 0);
  428   	this.set(Calendar.MILLISECOND, 0);
  429   	this.add(Calendar.WEEK_OF_YEAR, 1);
  430   	break;
  431       case DailyRollingFileAppender.TOP_OF_MONTH:
  432   	this.set(Calendar.DATE, 1);
  433   	this.set(Calendar.HOUR_OF_DAY, 0);
  434   	this.set(Calendar.MINUTE, 0);
  435   	this.set(Calendar.SECOND, 0);
  436   	this.set(Calendar.MILLISECOND, 0);
  437   	this.add(Calendar.MONTH, 1);
  438   	break;
  439       default:
  440   	throw new IllegalStateException("Unknown periodicity type.");
  441       }
  442       return getTime();
  443     }
  444   }

Save This Page
Home » apache-log4j-1.2.15 » org.apache » log4j » [javadoc | source]