KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > logging > jdk > handlers > DailyRollingFileHandler


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

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

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

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

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

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

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

207    public void setDatePattern(String JavaDoc pattern)
208    {
209       datePattern = pattern;
210    }
211
212    /**
213     * Returns the value of the <b>DatePattern</b> option.
214     */

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

282    int computeCheckPeriod()
283    {
284       RollingCalendar rollingCalendar = new RollingCalendar(gmtTimeZone, Locale.ENGLISH);
285       // set sate to 1970-01-01 00:00:00 GMT
286
Date JavaDoc epoch = new Date JavaDoc(0);
287       if (datePattern != null)
288       {
289          for (int i = TOP_OF_MINUTE; i <= TOP_OF_MONTH; i++)
290          {
291             SimpleDateFormat JavaDoc simpleDateFormat = new SimpleDateFormat JavaDoc(datePattern);
292             simpleDateFormat.setTimeZone(gmtTimeZone); // do all date formatting in GMT
293
String JavaDoc r0 = simpleDateFormat.format(epoch);
294             rollingCalendar.setType(i);
295             Date JavaDoc next = new Date JavaDoc(rollingCalendar.getNextCheckMillis(epoch));
296             String JavaDoc r1 = simpleDateFormat.format(next);
297             //System.out.println("Type = "+i+", r0 = "+r0+", r1 = "+r1);
298
if (r0 != null && r1 != null && !r0.equals(r1))
299             {
300                return i;
301             }
302          }
303       }
304       return TOP_OF_TROUBLE; // Deliberately head for trouble...
305
}
306
307    /**
308     * Rollover the current file to a new file.
309     */

310    void rollOver() throws IOException JavaDoc
311    {
312
313       /* Compute filename, but only if datePattern is specified */
314       if (datePattern == null)
315       {
316          reportError("Missing DatePattern option in rollOver().", null, ErrorManager.OPEN_FAILURE);
317          return;
318       }
319
320       String JavaDoc datedFilename = fileName + sdf.format(now);
321       // It is too early to roll over because we are still within the
322
// bounds of the current interval. Rollover will occur once the
323
// next interval is reached.
324
if (scheduledFilename.equals(datedFilename))
325       {
326          return;
327       }
328
329       // close current file, and rename it to datedFilename
330
this.close();
331
332       File JavaDoc target = new File JavaDoc(scheduledFilename);
333       if (target.exists())
334       {
335          target.delete();
336       }
337
338       File JavaDoc file = new File JavaDoc(fileName);
339       boolean result = file.renameTo(target);
340       if (result)
341       {
342          debug(fileName + " -> " + scheduledFilename);
343       }
344       else
345       {
346          reportError("Failed to rename [" + fileName + "] to [" + scheduledFilename + "].",
347             null, ErrorManager.OPEN_FAILURE);
348       }
349
350       try
351       {
352          // This will also close the file. This is OK since multiple
353
// close operations are safe.
354
this.setFile(fileName, false, this.bufferedIO, this.bufferSize);
355       }
356       catch (IOException JavaDoc e)
357       {
358          reportError("setFile(" + fileName + ", false) call failed.",
359             null, ErrorManager.OPEN_FAILURE);
360       }
361       scheduledFilename = datedFilename;
362    }
363
364    /**
365     * This method differentiates DailyRollingFileAppender from its
366     * super class.
367     * <p/>
368     * <p>Before actually logging, this method will check whether it is
369     * time to do a rollover. If it is, it will schedule the next
370     * rollover time and then rollover.
371     */

372    protected void subPublish(LogRecord JavaDoc event)
373    {
374       long n = System.currentTimeMillis();
375       if (n >= nextCheck)
376       {
377          now.setTime(n);
378          nextCheck = rc.getNextCheckMillis(now);
379          try
380          {
381             rollOver();
382          }
383          catch (IOException JavaDoc ioe)
384          {
385             reportError("rollOver() failed.", ioe, ErrorManager.OPEN_FAILURE);
386          }
387       }
388       super.subPublish(event);
389    }
390 }
391
392 /**
393  * RollingCalendar is a helper class to DailyRollingFileAppender.
394  * Given a periodicity type and the current time, it computes the
395  * start of the next interval.
396  * */

397 class RollingCalendar extends GregorianCalendar JavaDoc
398 {
399
400    int type = DailyRollingFileHandler.TOP_OF_TROUBLE;
401
402    RollingCalendar()
403    {
404       super();
405    }
406
407    RollingCalendar(TimeZone JavaDoc tz, Locale JavaDoc locale)
408    {
409       super(tz, locale);
410    }
411
412    void setType(int type)
413    {
414       this.type = type;
415    }
416
417    public long getNextCheckMillis(Date JavaDoc now)
418    {
419       return getNextCheckDate(now).getTime();
420    }
421
422    public Date JavaDoc getNextCheckDate(Date JavaDoc now)
423    {
424       this.setTime(now);
425
426       switch (type)
427       {
428          case DailyRollingFileHandler.TOP_OF_MINUTE:
429             this.set(Calendar.SECOND, 0);
430             this.set(Calendar.MILLISECOND, 0);
431             this.add(Calendar.MINUTE, 1);
432             break;
433          case DailyRollingFileHandler.TOP_OF_HOUR:
434             this.set(Calendar.MINUTE, 0);
435             this.set(Calendar.SECOND, 0);
436             this.set(Calendar.MILLISECOND, 0);
437             this.add(Calendar.HOUR_OF_DAY, 1);
438             break;
439          case DailyRollingFileHandler.HALF_DAY:
440             this.set(Calendar.MINUTE, 0);
441             this.set(Calendar.SECOND, 0);
442             this.set(Calendar.MILLISECOND, 0);
443             int hour = get(Calendar.HOUR_OF_DAY);
444             if (hour < 12)
445             {
446                this.set(Calendar.HOUR_OF_DAY, 12);
447             }
448             else
449             {
450                this.set(Calendar.HOUR_OF_DAY, 0);
451                this.add(Calendar.DAY_OF_MONTH, 1);
452             }
453             break;
454          case DailyRollingFileHandler.TOP_OF_DAY:
455             this.set(Calendar.HOUR_OF_DAY, 0);
456             this.set(Calendar.MINUTE, 0);
457             this.set(Calendar.SECOND, 0);
458             this.set(Calendar.MILLISECOND, 0);
459             this.add(Calendar.DATE, 1);
460             break;
461          case DailyRollingFileHandler.TOP_OF_WEEK:
462             this.set(Calendar.DAY_OF_WEEK, getFirstDayOfWeek());
463             this.set(Calendar.HOUR_OF_DAY, 0);
464             this.set(Calendar.SECOND, 0);
465             this.set(Calendar.MILLISECOND, 0);
466             this.add(Calendar.WEEK_OF_YEAR, 1);
467             break;
468          case DailyRollingFileHandler.TOP_OF_MONTH:
469             this.set(Calendar.DATE, 1);
470             this.set(Calendar.HOUR_OF_DAY, 0);
471             this.set(Calendar.SECOND, 0);
472             this.set(Calendar.MILLISECOND, 0);
473             this.add(Calendar.MONTH, 1);
474             break;
475          default:
476             throw new IllegalStateException JavaDoc("Unknown periodicity type.");
477       }
478       return getTime();
479   }
480 }
481
482
Popular Tags