一个好老好老的老程序员了。
全部博文(915)
分类: Java
2011-12-06 16:41:23
Custom DailyRollingFileAppender with MaxBackupIndex
I've change the DailyRollingFileAppender to support the , this is the class to add to the jar that contains the log4j library:
The rolling schedule is specified by the DatePattern.
37 This pattern should follow the {@link SimpleDateFormat} conventions. 38 In particular, you must 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 42For example, if the File option is set to
43/foo/bar.log
and the DatePattern set to
44 '.'yyyy-MM-dd
, on 2001-02-16 at midnight, the logging
45 file /foo/bar.log
will be copied to
46 /foo/bar.log.2001-02-16
and logging for 2001-02-17
47 will continue in /foo/bar.log
until it rolls over
48 the next day.
49
50 Is is possible to specify monthly, weekly, half-daily, daily,
51 hourly, or minutely rollover schedules. 52 53DatePattern | 56Rollover schedule | 57Example | 58 59||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
'.'yyyy-MM
61 | Rollover at the beginning of each month | 62 63At midnight of May 31st, 2002 /foo/bar.log will be
64 copied to /foo/bar.log.2002-05 . Logging for the month
65 of June will be output to /foo/bar.log until it is
66 also rolled over the next month.
67
68 '.'yyyy-ww
70
71 Rollover at the first day of each week. The first day of the
72 week depends on the locale. |
73
74 Assuming the first day of the week is Sunday, on Saturday
75 midnight, June 9th 2002, the file /foo/bar.log will be
76 copied to /foo/bar.log.2002-23. Logging for the 24th week
77 of 2002 will be output to | /foo/bar.log until it is
78 rolled over the next week.
79
80 '.'yyyy-MM-dd
82
83 Rollover at midnight each day. |
84
85 At midnight, on March 8th, 2002, | /foo/bar.log will
86 be copied to /foo/bar.log.2002-03-08 . Logging for the
87 9th day of March will be output to /foo/bar.log until
88 it is rolled over the next day.
89
90 '.'yyyy-MM-dd-a
92
93 Rollover at midnight and midday of each day. |
94
95 At noon, on March 9th, 2002, | /foo/bar.log will be
96 copied to /foo/bar.log.2002-03-09-AM . Logging for the
97 afternoon of the 9th will be output to /foo/bar.log
98 until it is rolled over at midnight.
99
100 '.'yyyy-MM-dd-HH
102
103 Rollover at the top of every hour. |
104
105 At approximately 11:00.000 o'clock on March 9th, 2002,
106 | /foo/bar.log will be copied to
107 /foo/bar.log.2002-03-09-10 . Logging for the 11th hour
108 of the 9th of March will be output to /foo/bar.log
109 until it is rolled over at the beginning of the next hour.
110
111
112 '.'yyyy-MM-dd-HH-mm
114
115 Rollover at the beginning of every minute. |
116
117 At approximately 11:23,000, on March 9th, 2001,
118 | /foo/bar.log will be copied to
119 /foo/bar.log.2001-03-09-10-22 . Logging for the minute
120 of 11:23 (9th of March) will be output to
121 /foo/bar.log until it is rolled over the next minute.
122
123 |
Do not use the colon ":" character in anywhere in the
126 DatePattern 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 130You have also to define the maximum number of file are kept
131 before the oldest is erased. 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 aDailyRollingFileAppender
and open the
194 file designated by filename
. 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 DatePattern 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 DatePattern option. */
217 public String getDatePattern() {
218 return datePattern;
219 }
220
221 /**
222 Set the maximum number of backup files to keep around.
223
224 The MaxBackupIndex 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 MaxBackupIndex 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 *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 }