Configuration Example
<appender name="FlatFileAppender" class="org.apache.log4j.DailyRollingFileAppender">
<param name="append" value="true" />
<param name="encoding" value="UTF-8" />
<param name="file" value="mylogfile.log" />
<param name="DatePattern" value="'.'yyyy-MM-dd" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="[%-25d{ISO8601}] %-5p %x %C{1} -- %m\n" />
</layout>
</appender>
Custom DailyRollingFileAppender with MaxBackupIndex
I've change the DailyRollingFileAppender to support the MaxBackupIndex, this is the class to add to the jar that contains the log4j library:
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;
22 import java.io.File;
23 import java.text.SimpleDateFormat;
24 import java.util.Date;
25 import java.util.Calendar;
26 import java.util.TimeZone;
27 import java.util.Locale;
28
29 import org.apache.log4j.helpers.LogLog;
30 import org.apache.log4j.spi.LoggingEvent;
31
32 /**
33 DailyMaxRollingFileAppender extends {@link FileAppender} so that the
34 underlying file is rolled over at a user chosen frequency.
35
36 <p>The rolling schedule is specified by the <b>DatePattern</b>.
37 This pattern should follow the {@link SimpleDateFormat} conventions.
38 In particular, you <em>must</em> escape literal text within a pair
39 of single quotes. A formatted version of the date pattern is used
40 as the suffix for the rolled file name.
41
42 <p>For example, if the <b>File</b> option is set to
43 <code>/foo/bar.log</code> and the <b>DatePattern</b> set to
44 <code>'.'yyyy-MM-dd</code>, on 2001-02-16 at midnight, the logging
45 file <code>/foo/bar.log</code> will be copied to
46 <code>/foo/bar.log.2001-02-16</code> and logging for 2001-02-17
47 will continue in <code>/foo/bar.log</code> until it rolls over
48 the next day.
49
50 <p>Is is possible to specify monthly, weekly, half-daily, daily,
51 hourly, or minutely rollover schedules.
52
53 <p><table border="1" cellpadding="2">
54 <tr>
55 <th>DatePattern</th>
56 <th>Rollover schedule</th>
57 <th>Example</th>
58
59 <tr>
60 <td><code>'.'yyyy-MM</code>
61 <td>Rollover at the beginning of each month</td>
62
63 <td>At midnight of May 31st, 2002 <code>/foo/bar.log</code> will be
64 copied to <code>/foo/bar.log.2002-05</code>. Logging for the month
65 of June will be output to <code>/foo/bar.log</code> until it is
66 also rolled over the next month.
67
68 <tr>
69 <td><code>'.'yyyy-ww</code>
70
71 <td>Rollover at the first day of each week. The first day of the
72 week depends on the locale.</td>
73
74 <td>Assuming the first day of the week is Sunday, on Saturday
75 midnight, June 9th 2002, the file <i>/foo/bar.log</i> will be
76 copied to <i>/foo/bar.log.2002-23</i>. Logging for the 24th week
77 of 2002 will be output to <code>/foo/bar.log</code> until it is
78 rolled over the next week.
79
80 <tr>
81 <td><code>'.'yyyy-MM-dd</code>
82
83 <td>Rollover at midnight each day.</td>
84
85 <td>At midnight, on March 8th, 2002, <code>/foo/bar.log</code> will
86 be copied to <code>/foo/bar.log.2002-03-08</code>. Logging for the
87 9th day of March will be output to <code>/foo/bar.log</code> until
88 it is rolled over the next day.
89
90 <tr>
91 <td><code>'.'yyyy-MM-dd-a</code>
92
93 <td>Rollover at midnight and midday of each day.</td>
94
95 <td>At noon, on March 9th, 2002, <code>/foo/bar.log</code> will be
96 copied to <code>/foo/bar.log.2002-03-09-AM</code>. Logging for the
97 afternoon of the 9th will be output to <code>/foo/bar.log</code>
98 until it is rolled over at midnight.
99
100 <tr>
101 <td><code>'.'yyyy-MM-dd-HH</code>
102
103 <td>Rollover at the top of every hour.</td>
104
105 <td>At approximately 11:00.000 o'clock on March 9th, 2002,
106 <code>/foo/bar.log</code> will be copied to
107 <code>/foo/bar.log.2002-03-09-10</code>. Logging for the 11th hour
108 of the 9th of March will be output to <code>/foo/bar.log</code>
109 until it is rolled over at the beginning of the next hour.
110
111
112 <tr>
113 <td><code>'.'yyyy-MM-dd-HH-mm</code>
114
115 <td>Rollover at the beginning of every minute.</td>
116
117 <td>At approximately 11:23,000, on March 9th, 2001,
118 <code>/foo/bar.log</code> will be copied to
119 <code>/foo/bar.log.2001-03-09-10-22</code>. Logging for the minute
120 of 11:23 (9th of March) will be output to
121 <code>/foo/bar.log</code> until it is rolled over the next minute.
122
123 </table>
124
125 <p>Do not use the colon ":" character in anywhere in the
126 <b>DatePattern</b> option. The text before the colon is interpeted
127 as the protocol specificaion of a URL which is probably not what
128 you want.
129
130 <p>You have also to define the maximum number of file are kept
131 before the oldest is erased.</p>
132
133 @author Eirik Lygre
134 @author Ceki Gülcü
135 @author Riccardo Nicosia;
136 */
137 public class DailyMaxRollingFileAppender extends FileAppender
138 {
139 // The code assumes that the following constants are in a increasing
140 // sequence.
141 static final int TOP_OF_TROUBLE=-1;
142 static final int TOP_OF_MINUTE = 0;
143 static final int TOP_OF_HOUR = 1;
144 static final int HALF_DAY = 2;
145 static final int TOP_OF_DAY = 3;
146 static final int TOP_OF_WEEK = 4;
147 static final int TOP_OF_MONTH = 5;
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 There is one backup file by default.
157 */
158 private int maxBackupIndex = 1;
159
160 /**
161 The log file will be renamed to the value of the
162 scheduledFilename variable when the next interval is entered. For
163 example, if the rollover period is one hour, the log file will be
164 renamed to the value of "scheduledFilename" at the beginning of
165 the next hour.
166
167 The precise time when a rollover occurs depends on logging
168 activity.
169 */
170 private String scheduledFilename;
171
172 /**
173 The next time we estimate a rollover should occur. */
174 private long nextCheck = System.currentTimeMillis () - 1;
175
176 Date now = new Date();
177
178 SimpleDateFormat sdf;
179
180 RollingPastCalendar rpc = new RollingPastCalendar();
181
182 int checkPeriod = TOP_OF_TROUBLE;
183
184 // The gmtTimeZone is used only in computeCheckPeriod() method.
185 static final TimeZone gmtTimeZone = TimeZone.getTimeZone("GMT");
186
187 /**
188 The default constructor does nothing. */
189 public DailyMaxRollingFileAppender()
190 {}
191
192 /**
193 Instantiate a <code>DailyRollingFileAppender</code> and open the
194 file designated by <code>filename</code>. The opened filename will
195 become the ouput destination for this appender.
196
197 */
198 public DailyMaxRollingFileAppender (Layout layout, String filename,
199 String datePattern)
200 throws IOException
201 {
202 super(layout, filename, true);
203 this.datePattern = datePattern;
204 activateOptions();
205 }
206
207 /**
208 The <b>DatePattern</b> takes a string in the same format as
209 expected by {@link SimpleDateFormat}. This options determines the
210 rollover schedule.
211 */
212 public void setDatePattern(String pattern) {
213 datePattern = pattern;
214 }
215
216 /** Returns the value of the <b>DatePattern</b> option. */
217 public String getDatePattern() {
218 return datePattern;
219 }
220
221 /**
222 Set the maximum number of backup files to keep around.
223
224 <p>The <b>MaxBackupIndex</b> option determines how many backup
225 files are kept before the oldest is erased. This option takes
226 a positive integer value. If set to zero, then there will be no
227 backup files and the log file will be renamed to the value of the
228 scheduledFilename variable when the next interval is entered.
229 */
230 public void setMaxBackupIndex(int maxBackups)
231 {
232 this.maxBackupIndex = maxBackups;
233 }
234
235 /**
236 Returns the value of the <b>MaxBackupIndex</b> option.
237 */
238 public int getMaxBackupIndex() {
239 return maxBackupIndex;
240 }
241
242 public void activateOptions()
243 {
244 super.activateOptions();
245
246 LogLog.debug("Max backup file kept: "+ maxBackupIndex + ".");
247
248 if(datePattern != null && fileName != null)
249 {
250 now.setTime(System.currentTimeMillis());
251 sdf = new SimpleDateFormat(datePattern);
252 int type = computeCheckPeriod();
253 printPeriodicity(type);
254 rpc.setType(type);
255 File file = new File(fileName);
256 scheduledFilename = fileName+sdf.format(new Date(file.lastModified()));
257 }
258 else
259 {
260 LogLog.error("Either File or DatePattern options are not set for appender ["
261 +name+"].");
262 }
263 }
264
265 void printPeriodicity(int type)
266 {
267 switch(type) {
268 case TOP_OF_MINUTE:
269 LogLog.debug("Appender [[+name+]] to be rolled every minute.");
270 break;
271 case TOP_OF_HOUR:
272 LogLog.debug("Appender ["+name
273 +"] to be rolled on top of every hour.");
274 break;
275 case HALF_DAY:
276 LogLog.debug("Appender ["+name
277 +"] to be rolled at midday and midnight.");
278 break;
279 case TOP_OF_DAY:
280 LogLog.debug("Appender ["+name
281 +"] to be rolled at midnight.");
282 break;
283 case TOP_OF_WEEK:
284 LogLog.debug("Appender ["+name
285 +"] to be rolled at start of week.");
286 break;
287 case TOP_OF_MONTH:
288 LogLog.debug("Appender ["+name
289 +"] to be rolled at start of every month.");
290 break;
291 default:
292 LogLog.warn("Unknown periodicity for appender [[+name+]].");
293 }
294 }
295
296 // This method computes the roll over period by looping over the
297 // periods, starting with the shortest, and stopping when the r0 is
298 // different from from r1, where r0 is the epoch formatted according
299 // the datePattern (supplied by the user) and r1 is the
300 // epoch+nextMillis(i) formatted according to datePattern. All date
301 // formatting is done in GMT and not local format because the test
302 // logic is based on comparisons relative to 1970-01-01 00:00:00
303 // GMT (the epoch).
304
305 int computeCheckPeriod()
306 {
307 RollingPastCalendar rollingPastCalendar = new RollingPastCalendar(gmtTimeZone, Locale.ENGLISH);
308 // set sate to 1970-01-01 00:00:00 GMT
309 Date epoch = new Date(0);
310 if(datePattern != null)
311 {
312 for(int i = TOP_OF_MINUTE; i <= TOP_OF_MONTH; i++)
313 {
314 SimpleDateFormat simpleDateFormat = new SimpleDateFormat(datePattern);
315 simpleDateFormat.setTimeZone(gmtTimeZone); // do all date formatting in GMT
316 String r0 = simpleDateFormat.format(epoch);
317 rollingPastCalendar.setType(i);
318 Date next = new Date(rollingPastCalendar.getNextCheckMillis(epoch));
319 String r1 = simpleDateFormat.format(next);
320
321 //System.out.println("Type = "+i+", r0 = "+r0+", r1 = "+r1);
322 if(r0 != null && r1 != null && !r0.equals(r1))
323 {
324 return i;
325 }
326 }
327 }
328
329 return TOP_OF_TROUBLE; // Deliberately head for trouble...
330 }
331
332 /**
333 Rollover the current file to a new file.
334 */
335 void rollOver() throws IOException
336 {
337 /* Compute filename, but only if datePattern is specified */
338 if (datePattern == null) {
339 errorHandler.error("Missing DatePattern option in rollOver().");
340 return;
341 }
342
343 String datedFilename = fileName+sdf.format(now);
344 // It is too early to roll over because we are still within the
345 // bounds of the current interval. Rollover will occur once the
346 // next interval is reached.
347 if (scheduledFilename.equals(datedFilename)) {
348 return;
349 }
350
351 // close current file, and rename it to datedFilename
352 this.closeFile();
353
354 File target = new File(scheduledFilename);
355 if (target.exists()) {
356 target.delete();
357 }
358
359 File file = new File(fileName);
360 boolean result = file.renameTo(target);
361 if(result)
362 {
363 LogLog.debug(fileName +" -> "+ scheduledFilename);
364
365 // If maxBackups <= 0, then there is no file renaming to be done.
366 if(maxBackupIndex > 0)
367 {
368 // Delete the oldest file, to keep Windows happy.
369 file = new File(fileName + dateBefore());
370
371 if (file.exists())
372 file.delete();
373 }
374 }
375 else
376 {
377 LogLog.error("Failed to rename [[+fileName+]] to [[+scheduledFilename+]].");
378 }
379
380 try
381 {
382 // This will also close the file. This is OK since multiple
383 // close operations are safe.
384 this.setFile(fileName, false, this.bufferedIO, this.bufferSize);
385 }
386 catch(IOException e) {
387 errorHandler.error("setFile("+fileName+", false) call failed.");
388 }
389 scheduledFilename = datedFilename;
390 }
391
392 private String dateBefore()
393 {
394 String dataAnte = "";
395
396
397 if(datePattern != null)
398 {
399 SimpleDateFormat simpleDateFormat = new SimpleDateFormat(datePattern);
400
401 dataAnte = simpleDateFormat.format(new Date(rpc.getPastCheckMillis(new Date(), maxBackupIndex)));
402 }
403
404 return dataAnte;
405 }
406
407 /**
408 * This method differentiates DailyRollingFileAppender from its
409 * super class.
410 *
411 * <p>Before actually logging, this method will check whether it is
412 * time to do a rollover. If it is, it will schedule the next
413 * rollover time and then rollover.
414 * */
415 protected void subAppend(LoggingEvent event)
416 {
417 long n = System.currentTimeMillis();
418
419 if (n >= nextCheck)
420 {
421 now.setTime(n);
422 nextCheck = rpc.getNextCheckMillis(now);
423
424 try
425 {
426 rollOver();
427 }
428 catch(IOException ioe)
429 {
430 LogLog.error("rollOver() failed.", ioe);
431 }
432 }
433
434 super.subAppend(event);
435 }
436
437 /*
438 * DEBUG
439 */
440 public static void main(String args[])
441 {
442 DailyMaxRollingFileAppender dmrfa = new DailyMaxRollingFileAppender();
443
444 dmrfa.setDatePattern("'.'yyyy-MM-dd-HH-mm");
445
446 dmrfa.setFile("prova");
447
448 System.out.println("dmrfa.getMaxBackupIndex():" + dmrfa.getMaxBackupIndex());
449
450 dmrfa.activateOptions();
451
452 for(int i = 0; i < 5; i++)
453 {
454 dmrfa.subAppend(null);
455
456 try
457 {
458 Thread.sleep(60000);
459 }
460 catch (InterruptedException ex)
461 {
462 }
463
464 System.out.println("Fine attesa");
465 }
466 }
467 }
468
469 /**
470 * RollingPastCalendar is a helper class to DailyMaxRollingFileAppender.
471 * Given a periodicity type and the current time, it computes the
472 * past maxBackupIndex date.
473 * */
474 class RollingPastCalendar extends RollingCalendar
475 {
476 RollingPastCalendar() {
477 super();
478 }
479
480 RollingPastCalendar(TimeZone tz, Locale locale) {
481 super(tz, locale);
482 }
483
484 public long getPastCheckMillis(Date now, int maxBackupIndex)
485 {
486 return getPastDate(now, maxBackupIndex).getTime();
487 }
488
489 public Date getPastDate(Date now, int maxBackupIndex)
490 {
491 this.setTime(now);
492
493 switch(type)
494 {
495 case DailyRollingFileAppender.TOP_OF_MINUTE:
496 this.set(Calendar.SECOND, this.get(Calendar.SECOND));
497 this.set(Calendar.MILLISECOND, this.get(Calendar.MILLISECOND));
498 this.set(Calendar.MINUTE, this.get(Calendar.MINUTE) - maxBackupIndex);
499 break;
500
501 case DailyRollingFileAppender.TOP_OF_HOUR:
502 this.set(Calendar.MINUTE, this.get(Calendar.MINUTE));
503 this.set(Calendar.SECOND, this.get(Calendar.SECOND));
504 this.set(Calendar.MILLISECOND, this.get(Calendar.MILLISECOND));
505 this.set(Calendar.HOUR_OF_DAY, this.get(Calendar.HOUR_OF_DAY) - maxBackupIndex);
506 break;
507
508 case DailyRollingFileAppender.HALF_DAY:
509 this.set(Calendar.MINUTE, this.get(Calendar.MINUTE));
510 this.set(Calendar.SECOND, this.get(Calendar.SECOND));
511 this.set(Calendar.MILLISECOND, this.get(Calendar.MILLISECOND));
512 int hour = get(Calendar.HOUR_OF_DAY);
513 if(hour < 12)
514 {
515 this.set(Calendar.HOUR_OF_DAY, 12);
516 }
517 else
518 {
519 this.set(Calendar.HOUR_OF_DAY, 0);
520 }
521 this.set(Calendar.DAY_OF_MONTH, this.get(Calendar.DAY_OF_MONTH) - maxBackupIndex);
522
523 break;
524
525 case DailyRollingFileAppender.TOP_OF_DAY:
526 this.set(Calendar.HOUR_OF_DAY, this.get(Calendar.HOUR_OF_DAY));
527 this.set(Calendar.MINUTE, this.get(Calendar.MINUTE));
528 this.set(Calendar.SECOND, this.get(Calendar.SECOND));
529 this.set(Calendar.MILLISECOND, this.get(Calendar.MILLISECOND));
530 this.set(Calendar.DATE, this.get(Calendar.DATE) - maxBackupIndex);
531 break;
532
533 case DailyRollingFileAppender.TOP_OF_WEEK:
534 this.set(Calendar.DAY_OF_WEEK, getFirstDayOfWeek());
535 this.set(Calendar.HOUR_OF_DAY, this.get(Calendar.HOUR_OF_DAY));
536 this.set(Calendar.MINUTE, this.get(Calendar.MINUTE));
537 this.set(Calendar.SECOND, this.get(Calendar.SECOND));
538 this.set(Calendar.MILLISECOND, this.get(Calendar.MILLISECOND));
539 this.set(Calendar.WEEK_OF_YEAR, this.get(Calendar.WEEK_OF_YEAR) - maxBackupIndex);
540 break;
541
542 case DailyRollingFileAppender.TOP_OF_MONTH:
543 this.set(Calendar.DATE, this.get(Calendar.DATE));
544 this.set(Calendar.HOUR_OF_DAY, this.get(Calendar.HOUR_OF_DAY));
545 this.set(Calendar.MINUTE, this.get(Calendar.MINUTE));
546 this.set(Calendar.SECOND, this.get(Calendar.SECOND));
547 this.set(Calendar.MILLISECOND, this.get(Calendar.MILLISECOND));
548 this.set(Calendar.MONTH, this.get(Calendar.MONTH) - maxBackupIndex);
549 break;
550
551 default:
552 throw new IllegalStateException("Unknown periodicity type.");
553 }
554
555 return getTime();
556 }
557 }
I'll hope can be usefull.