KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > log4j > DailyRollingFileAppender


1 /*
2  * Copyright 1999-2005 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17
18
19 package org.apache.log4j;
20
21 import java.io.IOException JavaDoc;
22 import java.io.File JavaDoc;
23 import java.text.SimpleDateFormat JavaDoc;
24 import java.util.Date JavaDoc;
25 import java.util.GregorianCalendar JavaDoc;
26 import java.util.Calendar JavaDoc;
27 import java.util.TimeZone JavaDoc;
28 import java.util.Locale JavaDoc;
29
30 import org.apache.log4j.helpers.LogLog;
31 import org.apache.log4j.spi.LoggingEvent;
32
33 /**
34    DailyRollingFileAppender extends {@link FileAppender} so that the
35    underlying file is rolled over at a user chosen frequency.
36
37    <p>The rolling schedule is specified by the <b>DatePattern</b>
38    option. This pattern should follow the {@link SimpleDateFormat}
39    conventions. In particular, you <em>must</em> escape literal text
40    within a pair of single quotes. A formatted version of the date
41    pattern is used as the suffix for the rolled file name.
42
43    <p>For example, if the <b>File</b> option is set to
44    <code>/foo/bar.log</code> and the <b>DatePattern</b> set to
45    <code>'.'yyyy-MM-dd</code>, on 2001-02-16 at midnight, the logging
46    file <code>/foo/bar.log</code> will be copied to
47    <code>/foo/bar.log.2001-02-16</code> and logging for 2001-02-17
48    will continue in <code>/foo/bar.log</code> until it rolls over
49    the next day.
50
51    <p>Is is possible to specify monthly, weekly, half-daily, daily,
52    hourly, or minutely rollover schedules.
53
54    <p><table border="1" cellpadding="2">
55    <tr>
56    <th>DatePattern</th>
57    <th>Rollover schedule</th>
58    <th>Example</th>
59
60    <tr>
61    <td><code>'.'yyyy-MM</code>
62    <td>Rollover at the beginning of each month</td>
63
64    <td>At midnight of May 31st, 2002 <code>/foo/bar.log</code> will be
65    copied to <code>/foo/bar.log.2002-05</code>. Logging for the month
66    of June will be output to <code>/foo/bar.log</code> until it is
67    also rolled over the next month.
68
69    <tr>
70    <td><code>'.'yyyy-ww</code>
71
72    <td>Rollover at the first day of each week. The first day of the
73    week depends on the locale.</td>
74
75    <td>Assuming the first day of the week is Sunday, on Saturday
76    midnight, June 9th 2002, the file <i>/foo/bar.log</i> will be
77    copied to <i>/foo/bar.log.2002-23</i>. Logging for the 24th week
78    of 2002 will be output to <code>/foo/bar.log</code> until it is
79    rolled over the next week.
80
81    <tr>
82    <td><code>'.'yyyy-MM-dd</code>
83
84    <td>Rollover at midnight each day.</td>
85
86    <td>At midnight, on March 8th, 2002, <code>/foo/bar.log</code> will
87    be copied to <code>/foo/bar.log.2002-03-08</code>. Logging for the
88    9th day of March will be output to <code>/foo/bar.log</code> until
89    it is rolled over the next day.
90
91    <tr>
92    <td><code>'.'yyyy-MM-dd-a</code>
93
94    <td>Rollover at midnight and midday of each day.</td>
95
96    <td>At noon, on March 9th, 2002, <code>/foo/bar.log</code> will be
97    copied to <code>/foo/bar.log.2002-03-09-AM</code>. Logging for the
98    afternoon of the 9th will be output to <code>/foo/bar.log</code>
99    until it is rolled over at midnight.
100
101    <tr>
102    <td><code>'.'yyyy-MM-dd-HH</code>
103
104    <td>Rollover at the top of every hour.</td>
105
106    <td>At approximately 11:00.000 o'clock on March 9th, 2002,
107    <code>/foo/bar.log</code> will be copied to
108    <code>/foo/bar.log.2002-03-09-10</code>. Logging for the 11th hour
109    of the 9th of March will be output to <code>/foo/bar.log</code>
110    until it is rolled over at the beginning of the next hour.
111
112
113    <tr>
114    <td><code>'.'yyyy-MM-dd-HH-mm</code>
115
116    <td>Rollover at the beginning of every minute.</td>
117
118    <td>At approximately 11:23,000, on March 9th, 2001,
119    <code>/foo/bar.log</code> will be copied to
120    <code>/foo/bar.log.2001-03-09-10-22</code>. Logging for the minute
121    of 11:23 (9th of March) will be output to
122    <code>/foo/bar.log</code> until it is rolled over the next minute.
123
124    </table>
125
126    <p>Do not use the colon ":" character in anywhere in the
127    <b>DatePattern</b> option. The text before the colon is interpeted
128    as the protocol specificaion of a URL which is probably not what
129    you want.
130
131
132    @author Eirik Lygre
133    @author Ceki G&uuml;lc&uuml; */

134 public class DailyRollingFileAppender extends FileAppender {
135
136
137   // The code assumes that the following constants are in a increasing
138
// sequence.
139
static final int TOP_OF_TROUBLE=-1;
140   static final int TOP_OF_MINUTE = 0;
141   static final int TOP_OF_HOUR = 1;
142   static final int HALF_DAY = 2;
143   static final int TOP_OF_DAY = 3;
144   static final int TOP_OF_WEEK = 4;
145   static final int TOP_OF_MONTH = 5;
146
147
148   /**
149      The date pattern. By default, the pattern is set to
150      "'.'yyyy-MM-dd" meaning daily rollover.
151    */

152   private String JavaDoc datePattern = "'.'yyyy-MM-dd";
153
154   /**
155      The log file will be renamed to the value of the
156      scheduledFilename variable when the next interval is entered. For
157      example, if the rollover period is one hour, the log file will be
158      renamed to the value of "scheduledFilename" at the beginning of
159      the next hour.
160
161      The precise time when a rollover occurs depends on logging
162      activity.
163   */

164   private String JavaDoc scheduledFilename;
165
166   /**
167      The next time we estimate a rollover should occur. */

168   private long nextCheck = System.currentTimeMillis () - 1;
169
170   Date JavaDoc now = new Date JavaDoc();
171
172   SimpleDateFormat JavaDoc sdf;
173
174   RollingCalendar rc = new RollingCalendar();
175
176   int checkPeriod = TOP_OF_TROUBLE;
177
178   // The gmtTimeZone is used only in computeCheckPeriod() method.
179
static final TimeZone JavaDoc gmtTimeZone = TimeZone.getTimeZone("GMT");
180
181
182   /**
183      The default constructor does nothing. */

184   public DailyRollingFileAppender() {
185   }
186
187   /**
188     Instantiate a <code>DailyRollingFileAppender</code> and open the
189     file designated by <code>filename</code>. The opened filename will
190     become the ouput destination for this appender.
191
192     */

193   public DailyRollingFileAppender (Layout layout, String JavaDoc filename,
194                    String JavaDoc datePattern) throws IOException JavaDoc {
195     super(layout, filename, true);
196     this.datePattern = datePattern;
197     activateOptions();
198   }
199
200   /**
201      The <b>DatePattern</b> takes a string in the same format as
202      expected by {@link SimpleDateFormat}. This options determines the
203      rollover schedule.
204    */

205   public void setDatePattern(String JavaDoc pattern) {
206     datePattern = pattern;
207   }
208
209   /** Returns the value of the <b>DatePattern</b> option. */
210   public String JavaDoc getDatePattern() {
211     return datePattern;
212   }
213
214   public void activateOptions() {
215     super.activateOptions();
216     if(datePattern != null && fileName != null) {
217       now.setTime(System.currentTimeMillis());
218       sdf = new SimpleDateFormat JavaDoc(datePattern);
219       int type = computeCheckPeriod();
220       printPeriodicity(type);
221       rc.setType(type);
222       File JavaDoc file = new File JavaDoc(fileName);
223       scheduledFilename = fileName+sdf.format(new Date JavaDoc(file.lastModified()));
224
225     } else {
226       LogLog.error("Either File or DatePattern options are not set for appender ["
227            +name+"].");
228     }
229   }
230
231   void printPeriodicity(int type) {
232     switch(type) {
233     case TOP_OF_MINUTE:
234       LogLog.debug("Appender ["+name+"] to be rolled every minute.");
235       break;
236     case TOP_OF_HOUR:
237       LogLog.debug("Appender ["+name
238            +"] to be rolled on top of every hour.");
239       break;
240     case HALF_DAY:
241       LogLog.debug("Appender ["+name
242            +"] to be rolled at midday and midnight.");
243       break;
244     case TOP_OF_DAY:
245       LogLog.debug("Appender ["+name
246            +"] to be rolled at midnight.");
247       break;
248     case TOP_OF_WEEK:
249       LogLog.debug("Appender ["+name
250            +"] to be rolled at start of week.");
251       break;
252     case TOP_OF_MONTH:
253       LogLog.debug("Appender ["+name
254            +"] to be rolled at start of every month.");
255       break;
256     default:
257       LogLog.warn("Unknown periodicity for appender ["+name+"].");
258     }
259   }
260
261
262   // This method computes the roll over period by looping over the
263
// periods, starting with the shortest, and stopping when the r0 is
264
// different from from r1, where r0 is the epoch formatted according
265
// the datePattern (supplied by the user) and r1 is the
266
// epoch+nextMillis(i) formatted according to datePattern. All date
267
// formatting is done in GMT and not local format because the test
268
// logic is based on comparisons relative to 1970-01-01 00:00:00
269
// GMT (the epoch).
270

271   int computeCheckPeriod() {
272     RollingCalendar rollingCalendar = new RollingCalendar(gmtTimeZone, Locale.ENGLISH);
273     // set sate to 1970-01-01 00:00:00 GMT
274
Date JavaDoc epoch = new Date JavaDoc(0);
275     if(datePattern != null) {
276       for(int i = TOP_OF_MINUTE; i <= TOP_OF_MONTH; i++) {
277     SimpleDateFormat JavaDoc simpleDateFormat = new SimpleDateFormat JavaDoc(datePattern);
278     simpleDateFormat.setTimeZone(gmtTimeZone); // do all date formatting in GMT
279
String JavaDoc r0 = simpleDateFormat.format(epoch);
280     rollingCalendar.setType(i);
281     Date JavaDoc next = new Date JavaDoc(rollingCalendar.getNextCheckMillis(epoch));
282     String JavaDoc r1 = simpleDateFormat.format(next);
283     //System.out.println("Type = "+i+", r0 = "+r0+", r1 = "+r1);
284
if(r0 != null && r1 != null && !r0.equals(r1)) {
285       return i;
286     }
287       }
288     }
289     return TOP_OF_TROUBLE; // Deliberately head for trouble...
290
}
291
292   /**
293      Rollover the current file to a new file.
294   */

295   void rollOver() throws IOException JavaDoc {
296
297     /* Compute filename, but only if datePattern is specified */
298     if (datePattern == null) {
299       errorHandler.error("Missing DatePattern option in rollOver().");
300       return;
301     }
302
303     String JavaDoc datedFilename = fileName+sdf.format(now);
304     // It is too early to roll over because we are still within the
305
// bounds of the current interval. Rollover will occur once the
306
// next interval is reached.
307
if (scheduledFilename.equals(datedFilename)) {
308       return;
309     }
310
311     // close current file, and rename it to datedFilename
312
this.closeFile();
313
314     File JavaDoc target = new File JavaDoc(scheduledFilename);
315     if (target.exists()) {
316       target.delete();
317     }
318
319     File JavaDoc file = new File JavaDoc(fileName);
320     boolean result = file.renameTo(target);
321     if(result) {
322       LogLog.debug(fileName +" -> "+ scheduledFilename);
323     } else {
324       LogLog.error("Failed to rename ["+fileName+"] to ["+scheduledFilename+"].");
325     }
326
327     try {
328       // This will also close the file. This is OK since multiple
329
// close operations are safe.
330
this.setFile(fileName, false, this.bufferedIO, this.bufferSize);
331     }
332     catch(IOException JavaDoc e) {
333       errorHandler.error("setFile("+fileName+", false) call failed.");
334     }
335     scheduledFilename = datedFilename;
336   }
337
338   /**
339    * This method differentiates DailyRollingFileAppender from its
340    * super class.
341    *
342    * <p>Before actually logging, this method will check whether it is
343    * time to do a rollover. If it is, it will schedule the next
344    * rollover time and then rollover.
345    * */

346   protected void subAppend(LoggingEvent event) {
347     long n = System.currentTimeMillis();
348     if (n >= nextCheck) {
349       now.setTime(n);
350       nextCheck = rc.getNextCheckMillis(now);
351       try {
352     rollOver();
353       }
354       catch(IOException JavaDoc ioe) {
355     LogLog.error("rollOver() failed.", ioe);
356       }
357     }
358     super.subAppend(event);
359    }
360 }
361
362 /**
363  * RollingCalendar is a helper class to DailyRollingFileAppender.
364  * Given a periodicity type and the current time, it computes the
365  * start of the next interval.
366  * */

367 class RollingCalendar extends GregorianCalendar JavaDoc {
368
369   int type = DailyRollingFileAppender.TOP_OF_TROUBLE;
370
371   RollingCalendar() {
372     super();
373   }
374
375   RollingCalendar(TimeZone JavaDoc tz, Locale JavaDoc locale) {
376     super(tz, locale);
377   }
378
379   void setType(int type) {
380     this.type = type;
381   }
382
383   public long getNextCheckMillis(Date JavaDoc now) {
384     return getNextCheckDate(now).getTime();
385   }
386
387   public Date JavaDoc getNextCheckDate(Date JavaDoc now) {
388     this.setTime(now);
389
390     switch(type) {
391     case DailyRollingFileAppender.TOP_OF_MINUTE:
392     this.set(Calendar.SECOND, 0);
393     this.set(Calendar.MILLISECOND, 0);
394     this.add(Calendar.MINUTE, 1);
395     break;
396     case DailyRollingFileAppender.TOP_OF_HOUR:
397     this.set(Calendar.MINUTE, 0);
398     this.set(Calendar.SECOND, 0);
399     this.set(Calendar.MILLISECOND, 0);
400     this.add(Calendar.HOUR_OF_DAY, 1);
401     break;
402     case DailyRollingFileAppender.HALF_DAY:
403     this.set(Calendar.MINUTE, 0);
404     this.set(Calendar.SECOND, 0);
405     this.set(Calendar.MILLISECOND, 0);
406     int hour = get(Calendar.HOUR_OF_DAY);
407     if(hour < 12) {
408       this.set(Calendar.HOUR_OF_DAY, 12);
409     } else {
410       this.set(Calendar.HOUR_OF_DAY, 0);
411       this.add(Calendar.DAY_OF_MONTH, 1);
412     }
413     break;
414     case DailyRollingFileAppender.TOP_OF_DAY:
415     this.set(Calendar.HOUR_OF_DAY, 0);
416     this.set(Calendar.MINUTE, 0);
417     this.set(Calendar.SECOND, 0);
418     this.set(Calendar.MILLISECOND, 0);
419     this.add(Calendar.DATE, 1);
420     break;
421     case DailyRollingFileAppender.TOP_OF_WEEK:
422     this.set(Calendar.DAY_OF_WEEK, getFirstDayOfWeek());
423     this.set(Calendar.HOUR_OF_DAY, 0);
424     this.set(Calendar.SECOND, 0);
425     this.set(Calendar.MILLISECOND, 0);
426     this.add(Calendar.WEEK_OF_YEAR, 1);
427     break;
428     case DailyRollingFileAppender.TOP_OF_MONTH:
429     this.set(Calendar.DATE, 1);
430     this.set(Calendar.HOUR_OF_DAY, 0);
431     this.set(Calendar.SECOND, 0);
432     this.set(Calendar.MILLISECOND, 0);
433     this.add(Calendar.MONTH, 1);
434     break;
435     default:
436     throw new IllegalStateException JavaDoc("Unknown periodicity type.");
437     }
438     return getTime();
439   }
440 }
441
Popular Tags