1 package org.apache.log4j;
2 
3 /*
4  * Licensed to the Apache Software Foundation (ASF) under one or more
5  * contributor license agreements.  See the NOTICE file distributed with
6  * this work for additional information regarding copyright ownership.
7  * The ASF licenses this file to You under the Apache License, Version 2.0
8  * (the "License"); you may not use this file except in compliance with
9  * the License.  You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 
20 import org.apache.log4j.RollingCalendar;
21 import org.apache.log4j.helpers.OptionConverter;
22 import org.apache.log4j.helpers.QuietWriter;
23 import org.apache.log4j.helpers.CountingQuietWriter;
24 import org.apache.log4j.helpers.LogLog;
25 import org.apache.log4j.spi.LoggingEvent;
26 
27 import java.util.Date;
28 import java.io.IOException;
29 import java.io.Writer;
30 import java.text.SimpleDateFormat;
31 import java.io.File;
32 import java.io.FilenameFilter;
33 
34 /**
35  *  <p>CompositeRollingAppender combines RollingFileAppender and DailyRollingFileAppender<br>
36  *  It can function as either or do both at the same time (making size
37  *  based rolling files like RollingFileAppender until a data/time boundary
38  *  is crossed at which time it rolls all of those files as per the DailyRollingFileAppender)
39  *  based on the setting for <code>rollingStyle</code>.<br>
40  *  <br>
41  *  To use CompositeRollingAppender to roll log files as they reach a certain
42  *  size (like RollingFileAppender), set rollingStyle=1 (@see config.size)<br>
43  *  To use CompositeRollingAppender to roll log files at certain time intervals
44  *  (daily for example), set rollingStyle=2 and a datePattern (@see config.time)<br>
45  *  To have CompositeRollingAppender roll log files at a certain size AND rename those
46  *  according to time intervals, set rollingStyle=3 (@see config.composite)<br>
47  *
48  *  <p>A of few additional optional features have been added:<br>
49  *  -- Attach date pattern for current log file (@see staticLogFileName)<br>
50  *  -- Backup number increments for newer files (@see countDirection)<br>
51  *  -- Infinite number of backups by file size (@see maxSizeRollBackups)<br>
52  *  <br>
53  *  <p>A few notes and warnings:  For large or infinite number of backups
54  *  countDirection > 0 is highly recommended, with staticLogFileName = false if
55  *  time based rolling is also used -- this will reduce the number of file renamings
56  *  to few or none.  Changing staticLogFileName or countDirection without clearing
57  *  the directory could have nasty side effects.  If Date/Time based rolling
58  *  is enabled, CompositeRollingAppender will attempt to roll existing files
59  *  in the directory without a date/time tag based on the last modified date
60  *  of the base log files last modification.<br>
61  *  <br>
62  *  <p>A maximum number of backups based on date/time boundries would be nice
63  *  but is not yet implemented.<br>
64  *
65  *  @author Kevin Steppe
66  *  @author Heinz Richter
67  *  @author Eirik Lygre
68  *  @author Ceki G&uuml;lc&uuml;
69  */
70 public class CompositeRollingAppender extends org.apache.log4j.FileAppender
71 {
72 	// The code assumes that the following 'time' constants are in a increasing
73 	// sequence.
74 	static final int TOP_OF_TROUBLE=-1;
75 	static final int TOP_OF_MINUTE = 0;
76 	static final int TOP_OF_HOUR   = 1;
77 	static final int HALF_DAY      = 2;
78 	static final int TOP_OF_DAY    = 3;
79 	static final int TOP_OF_WEEK   = 4;
80 	static final int TOP_OF_MONTH  = 5;
81 
82 	/** Style of rolling to use */
83 	static final int BY_SIZE = 1;
84 	static final int BY_DATE = 2;
85 	static final int BY_COMPOSITE = 3;
86 
87 	//Not currently used
88 	static final String S_BY_SIZE = "Size";
89 	static final String S_BY_DATE = "Date";
90 	static final String S_BY_COMPOSITE = "Composite";
91 
92 	/**
93 	 The date pattern. By default, the pattern is set to
94 	 "'.'yyyy-MM-dd" meaning daily rollover.
95 	*/
96 	private String datePattern = "'.'yyyy-MM-dd";
97 
98 	/**	 The actual formatted filename that is currently being written to
99 	     or will be the file transferred to on roll over
100 		 (based on staticLogFileName). */
101 	private String scheduledFilename = null;
102 
103 	/** The timestamp when we shall next recompute the filename. */
104 	private long nextCheck = System.currentTimeMillis () - 1;
105 
106 	/** Holds date of last roll over */
107 	Date now = new Date();
108 
109 	SimpleDateFormat sdf;
110 
111 	/** Helper class to determine next rollover time */
112 	RollingCalendar rc = new RollingCalendar();
113 
114 	/** Current period for roll overs */
115 	int checkPeriod = TOP_OF_TROUBLE;
116 
117 	/**	 The default maximum file size is 10MB. */
118 	protected long maxFileSize = 10*1024*1024;
119 
120 	/**	 There is zero backup files by default. */
121 	protected int maxSizeRollBackups = 0;
122 	/** How many sized based backups have been made so far */
123 	protected int curSizeRollBackups = 0;
124 
125 	/** not yet implemented */
126 	protected int maxTimeRollBackups = -1;
127 	protected int curTimeRollBackups = 0;
128 
129 	/** By default newer files have lower numbers. (countDirection < 0)
130 	 *  ie. log.1 is most recent, log.5 is the 5th backup, etc...
131 	 *  countDirection > 0 does the opposite ie.
132 	 *  log.1 is the first backup made, log.5 is the 5th backup made, etc.
133 	 *  For infinite backups use countDirection > 0 to reduce rollOver costs.
134 	 */
135 	protected int countDirection = -1;
136 
137 	/** Style of rolling to Use.  BY_SIZE (1), BY_DATE(2), BY COMPOSITE(3) */
138 	protected int rollingStyle = BY_COMPOSITE;
139 	protected boolean rollDate = true;
140 	protected boolean rollSize = true;
141 
142 	/** By default file.log is always the current file.  Optionally
143 	 *  file.log.yyyy-mm-dd for current formated datePattern can by the currently
144 	 *  logging file (or file.log.curSizeRollBackup or even
145 	 *  file.log.yyyy-mm-dd.curSizeRollBackup) This will make time based roll
146 	 *  overs with a large number of backups much faster -- it won't have to
147 	 *  rename all the backups!
148 	 */
149 	protected boolean staticLogFileName = true;
150 
151 	/** FileName provided in configuration.  Used for rolling properly */
152 	protected String baseFileName;
153 
154     /** The default constructor does nothing. */
CompositeRollingAppender()155 	public CompositeRollingAppender()  {
156     }
157 
158 	/**
159 	 Instantiate a <code>CompositeRollingAppender</code> and open the
160 	 file designated by <code>filename</code>. The opened filename will
161 	 become the ouput destination for this appender.
162 	*/
CompositeRollingAppender(Layout layout, String filename, String datePattern)163 	public CompositeRollingAppender (Layout layout, String filename,
164 				   String datePattern) throws IOException {
165 	    this(layout, filename, datePattern, true);
166 	}
167 
168 	/**
169 	 Instantiate a CompositeRollingAppender and open the file designated by
170 	 <code>filename</code>. The opened filename will become the ouput
171 	 destination for this appender.
172 
173 	 <p>If the <code>append</code> parameter is true, the file will be
174 	 appended to. Otherwise, the file desginated by
175 	 <code>filename</code> will be truncated before being opened.
176 	*/
CompositeRollingAppender(Layout layout, String filename, boolean append)177 	public CompositeRollingAppender(Layout layout, String filename, boolean append)
178 									  throws IOException {
179 	    super(layout, filename, append);
180 	}
181 
182 	/**
183 	 Instantiate a CompositeRollingAppender and open the file designated by
184 	 <code>filename</code>. The opened filename will become the ouput
185 	 destination for this appender.
186 	*/
CompositeRollingAppender(Layout layout, String filename, String datePattern, boolean append)187 	public CompositeRollingAppender (Layout layout, String filename,
188 				   String datePattern, boolean append) throws IOException {
189 	    super(layout, filename, append);
190 	    this.datePattern = datePattern;
191 		activateOptions();
192 	}
193 	/**
194 	 Instantiate a CompositeRollingAppender and open the file designated by
195 	 <code>filename</code>. The opened filename will become the output
196 	 destination for this appender.
197 
198 	 <p>The file will be appended to.  DatePattern is default.
199 	*/
CompositeRollingAppender(Layout layout, String filename)200 	public CompositeRollingAppender(Layout layout, String filename) throws IOException {
201 	    super(layout, filename);
202 	}
203 
204 	/**
205 	 The <b>DatePattern</b> takes a string in the same format as
206 	 expected by {@link SimpleDateFormat}. This options determines the
207 	 rollover schedule.
208 	*/
setDatePattern(String pattern)209 	public void setDatePattern(String pattern) {
210 	    datePattern = pattern;
211 	}
212 
213 	/** Returns the value of the <b>DatePattern</b> option. */
getDatePattern()214 	public String getDatePattern() {
215 	    return datePattern;
216 	}
217 
218 	/**
219 	 Returns the value of the <b>maxSizeRollBackups</b> option.
220 	*/
getMaxSizeRollBackups()221 	public int getMaxSizeRollBackups() {
222 	    return maxSizeRollBackups;
223 	}
224 
225 	/**
226 	 Get the maximum size that the output file is allowed to reach
227 	 before being rolled over to backup files.
228 
229 	 @since 1.1
230 	*/
getMaximumFileSize()231 	public long getMaximumFileSize() {
232 		return maxFileSize;
233 	}
234 
235 	/**
236 	 <p>Set the maximum number of backup files to keep around based on file size.
237 
238 	 <p>The <b>MaxSizeRollBackups</b> option determines how many backup
239 	 files are kept before the oldest is erased. This option takes
240 	 an integer value. If set to zero, then there will be no
241 	 backup files and the log file will be truncated when it reaches
242 	 <code>MaxFileSize</code>.  If a negative number is supplied then
243 	 no deletions will be made.  Note that this could result in
244 	 very slow performance as a large number of files are rolled over unless
245 	 {@link #setCountDirection} up is used.
246 
247 	 <p>The maximum applys to -each- time based group of files and -not- the total.
248 	 Using a daily roll the maximum total files would be (#days run) * (maxSizeRollBackups)
249 
250 	*/
setMaxSizeRollBackups(int maxBackups)251 	public void setMaxSizeRollBackups(int maxBackups) {
252 	    maxSizeRollBackups = maxBackups;
253 	}
254 
255 	/**
256 	 Set the maximum size that the output file is allowed to reach
257 	 before being rolled over to backup files.
258 
259 	 <p>This method is equivalent to {@link #setMaxFileSize} except
260 	 that it is required for differentiating the setter taking a
261 	 <code>long</code> argument from the setter taking a
262 	 <code>String</code> argument by the JavaBeans {@link
263 	 java.beans.Introspector Introspector}.
264 
265 	 @see #setMaxFileSize(String)
266 	*/
setMaxFileSize(long maxFileSize)267 	public void setMaxFileSize(long maxFileSize) {
268 	   this.maxFileSize = maxFileSize;
269 	}
270 
271 	/**
272 	 Set the maximum size that the output file is allowed to reach
273 	 before being rolled over to backup files.
274 
275 	 <p>This method is equivalent to {@link #setMaxFileSize} except
276 	 that it is required for differentiating the setter taking a
277 	 <code>long</code> argument from the setter taking a
278 	 <code>String</code> argument by the JavaBeans {@link
279 	 java.beans.Introspector Introspector}.
280 
281 	 @see #setMaxFileSize(String)
282 	*/
setMaximumFileSize(long maxFileSize)283 	public void setMaximumFileSize(long maxFileSize) {
284 		this.maxFileSize = maxFileSize;
285 	}
286 
287 	/**
288 	 Set the maximum size that the output file is allowed to reach
289 	 before being rolled over to backup files.
290 
291 	 <p>In configuration files, the <b>MaxFileSize</b> option takes an
292 	 long integer in the range 0 - 2^63. You can specify the value
293 	 with the suffixes "KB", "MB" or "GB" so that the integer is
294 	 interpreted being expressed respectively in kilobytes, megabytes
295 	 or gigabytes. For example, the value "10KB" will be interpreted
296 	 as 10240.
297 	*/
setMaxFileSize(String value)298 	public void setMaxFileSize(String value) {
299 	    maxFileSize = OptionConverter.toFileSize(value, maxFileSize + 1);
300 	}
301 
setQWForFiles(Writer writer)302 	protected void setQWForFiles(Writer writer) {
303 	    qw = new CountingQuietWriter(writer, errorHandler);
304 	}
305 
306 	//Taken verbatum from DailyRollingFileAppender
computeCheckPeriod()307 	int computeCheckPeriod() {
308 		RollingCalendar c = new RollingCalendar();
309 		// set sate to 1970-01-01 00:00:00 GMT
310 		Date epoch = new Date(0);
311 		if(datePattern != null) {
312 			for(int i = TOP_OF_MINUTE; i <= TOP_OF_MONTH; i++) {
313 				String r0 = sdf.format(epoch);
314 				c.setType(i);
315 				Date next = new Date(c.getNextCheckMillis(epoch));
316 				String r1 = sdf.format(next);
317 				//LogLog.debug("Type = "+i+", r0 = "+r0+", r1 = "+r1);
318 				if(r0 != null && r1 != null && !r0.equals(r1)) {
319 					return i;
320 				}
321 			}
322 		}
323 		return TOP_OF_TROUBLE; // Deliberately head for trouble...
324 	}
325 
326 	//Now for the new stuff
327 	/**
328 	 * Handles append time behavior for CompositeRollingAppender.  This checks
329 	 * if a roll over either by date (checked first) or time (checked second)
330 	 * is need and then appends to the file last.
331 	*/
subAppend(LoggingEvent event)332 	protected void subAppend(LoggingEvent event) {
333 
334 		if (rollDate) {
335 			long n = System.currentTimeMillis();
336 			if (n >= nextCheck) {
337 				now.setTime(n);
338 				nextCheck = rc.getNextCheckMillis(now);
339 
340 				rollOverTime();
341 			}
342 		}
343 
344 		if (rollSize) {
345 			if ((fileName != null) && ((CountingQuietWriter) qw).getCount() >= maxFileSize) {
346 			    rollOverSize();
347 			}
348 		}
349 
350 		super.subAppend(event);
351 	}
352 
setFile(String file)353 	public void setFile(String file)
354 	{
355 		baseFileName = file.trim();
356 		fileName = file.trim();
357 	}
358 
359 	/**
360 	 * Creates and opens the file for logging.  If <code>staticLogFileName</code>
361 	 * is false then the fully qualified name is determined and used.
362 	 */
setFile(String fileName, boolean append)363 	public synchronized void setFile(String fileName, boolean append) throws IOException {
364 		if (!staticLogFileName) {
365 		    scheduledFilename = fileName = fileName.trim() + sdf.format(now);
366 			if (countDirection > 0) {
367 				scheduledFilename = fileName = fileName + '.' + (++curSizeRollBackups);
368 			}
369 		}
370 
371 		super.setFile(fileName, append);
372 		if(append) {
373 		  File f = new File(fileName);
374 		  ((CountingQuietWriter) qw).setCount(f.length());
375 		}
376 	}
377 
getCountDirection()378 	public int getCountDirection() {
379 		return countDirection;
380 	}
381 
setCountDirection(int direction)382 	public void setCountDirection(int direction) {
383 		countDirection = direction;
384 	}
385 
getRollingStyle()386 	public int getRollingStyle () {
387         return rollingStyle;
388 	}
389 
setRollingStyle(int style)390 	public void setRollingStyle(int style) {
391 	    rollingStyle = style;
392 		switch (rollingStyle) {
393 			case BY_SIZE:
394 				 rollDate = false;
395 				 rollSize = true;
396 				 break;
397 			case BY_DATE:
398 				 rollDate = true;
399 				 rollSize = false;
400 				 break;
401 			case BY_COMPOSITE:
402 				 rollDate = true;
403 				 rollSize = true;
404 				 break;
405 			default:
406 				errorHandler.error("Invalid rolling Style, use 1 (by size only), 2 (by date only) or 3 (both)");
407 		}
408 	}
409 
410 /*
411 	public void setRollingStyle(String style) {
412 		if (style == S_BY_SIZE) {
413 		    rollingStyle = BY_SIZE;
414 		}
415 		else if (style == S_BY_DATE) {
416 		    rollingStyle = BY_DATE;
417 		}
418 		else if (style == S_BY_COMPOSITE) {
419 			rollingStyle = BY_COMPOSITE;
420 		}
421 	}
422 */
getStaticLogFileName()423 	public boolean getStaticLogFileName() {
424 	    return staticLogFileName;
425 	}
426 
setStaticLogFileName(boolean s)427 	public void setStaticLogFileName(boolean s) {
428 		staticLogFileName = s;
429 	}
430 
setStaticLogFileName(String value)431 	public void setStaticLogFileName(String value) {
432 		setStaticLogFileName(OptionConverter.toBoolean(value, true));
433 	}
434 
435 	/**
436 	 *  Initializes based on exisiting conditions at time of <code>
437 	 *  activateOptions</code>.  The following is done:<br>
438 	 *  <br>
439 	 *	A) determine curSizeRollBackups<br>
440 	 *	B) determine curTimeRollBackups (not implemented)<br>
441 	 *	C) initiates a roll over if needed for crossing a date boundary since
442 	 *  the last run.
443 	 */
existingInit()444 	protected void existingInit() {
445 
446 		curSizeRollBackups = 0;
447 		curTimeRollBackups = 0;
448 
449 		//part A starts here
450 		String filter;
451 		if (staticLogFileName || !rollDate) {
452 			filter = baseFileName + ".*";
453 		}
454 		else {
455 			filter = scheduledFilename + ".*";
456 		}
457 
458 		File f = new File(baseFileName);
459 		f = f.getParentFile();
460 		if (f == null)
461 		   f = new File(".");
462 
463 		LogLog.debug("Searching for existing files in: " + f);
464 		String[] files = f.list();
465 
466 		if (files != null) {
467 			for (int i = 0; i < files.length; i++) {
468 				if (!files[i].startsWith(baseFileName))
469 				   continue;
470 
471 				int index = files[i].lastIndexOf(".");
472 
473 				if (staticLogFileName) {
474 				   int endLength = files[i].length() - index;
475 				   if (baseFileName.length() + endLength != files[i].length()) {
476 					   //file is probably scheduledFilename + .x so I don't care
477 					   continue;
478 				   }
479 				}
480 
481 				try {
482 					int backup = Integer.parseInt(files[i].substring(index + 1, files[i].length()));
483 					LogLog.debug("From file: " + files[i] + " -> " + backup);
484 					if (backup > curSizeRollBackups)
485 					   curSizeRollBackups = backup;
486 				}
487 				catch (Exception e) {
488 					//this happens when file.log -> file.log.yyyy-mm-dd which is normal
489 					//when staticLogFileName == false
490 					LogLog.debug("Encountered a backup file not ending in .x " + files[i]);
491 				}
492 			}
493 		}
494 		LogLog.debug("curSizeRollBackups starts at: " + curSizeRollBackups);
495 		//part A ends here
496 
497 		//part B not yet implemented
498 
499 		//part C
500 		if (staticLogFileName && rollDate) {
501 			File old = new File(baseFileName);
502 			if (old.exists()) {
503 				Date last = new Date(old.lastModified());
504 				if (!(sdf.format(last).equals(sdf.format(now)))) {
505 					scheduledFilename = baseFileName + sdf.format(last);
506 					LogLog.debug("Initial roll over to: " + scheduledFilename);
507 					rollOverTime();
508 				}
509 			}
510 		}
511 		LogLog.debug("curSizeRollBackups after rollOver at: " + curSizeRollBackups);
512 		//part C ends here
513 
514 	}
515 
516 	/**
517 	 * Sets initial conditions including date/time roll over information, first check,
518 	 * scheduledFilename, and calls <code>existingInit</code> to initialize
519 	 * the current # of backups.
520 	 */
activateOptions()521 	public void activateOptions() {
522 
523 	    //REMOVE removed rollDate from boolean to enable Alex's change
524 		if(datePattern != null) {
525 			now.setTime(System.currentTimeMillis());
526 			sdf = new SimpleDateFormat(datePattern);
527 			int type = computeCheckPeriod();
528 			//printPeriodicity(type);
529 			rc.setType(type);
530 			//next line added as this removes the name check in rollOver
531 			nextCheck = rc.getNextCheckMillis(now);
532 		} else {
533 			if (rollDate)
534 			    LogLog.error("Either DatePattern or rollingStyle options are not set for ["+
535 			      name+"].");
536 		}
537 
538 		existingInit();
539 
540 		super.activateOptions();
541 
542 		if (rollDate && fileName != null && scheduledFilename == null)
543 			scheduledFilename = fileName + sdf.format(now);
544 	}
545 
546 	/**
547 	 Rollover the file(s) to date/time tagged file(s).
548 	 Opens the new file (through setFile) and resets curSizeRollBackups.
549 	*/
rollOverTime()550 	protected void rollOverTime() {
551 
552 	    curTimeRollBackups++;
553 
554 		//delete the old stuff here
555 
556 		if (staticLogFileName) {
557 			/* Compute filename, but only if datePattern is specified */
558 			if (datePattern == null) {
559 				errorHandler.error("Missing DatePattern option in rollOver().");
560 				return;
561 			}
562 
563 			//is the new file name equivalent to the 'current' one
564 			//something has gone wrong if we hit this -- we should only
565 			//roll over if the new file will be different from the old
566 			String dateFormat = sdf.format(now);
567 			if (scheduledFilename.equals(fileName + dateFormat)) {
568 				errorHandler.error("Compare " + scheduledFilename + " : " + fileName + dateFormat);
569 				return;
570 			}
571 
572 			// close current file, and rename it to datedFilename
573 			this.closeFile();
574 
575 			//we may have to roll over a large number of backups here
576 	        String from, to;
577 			for (int i = 1; i <= curSizeRollBackups; i++) {
578 				from = fileName + '.' + i;
579 				to = scheduledFilename + '.' + i;
580 				rollFile(from, to);
581 	        }
582 
583 			rollFile(fileName, scheduledFilename);
584 		}
585 
586 		try {
587 			// This will also close the file. This is OK since multiple
588 			// close operations are safe.
589 			curSizeRollBackups = 0; //We're cleared out the old date and are ready for the new
590 
591 			//new scheduled name
592 			scheduledFilename = fileName + sdf.format(now);
593 			this.setFile(baseFileName, false);
594 		}
595 		catch(IOException e) {
596 			errorHandler.error("setFile("+fileName+", false) call failed.");
597 		}
598 
599 	}
600 
601 	/** Renames file <code>from</code> to file <code>to</code>.  It
602 	 *  also checks for existence of target file and deletes if it does.
603 	 */
rollFile(String from, String to)604 	protected static void rollFile(String from, String to) {
605 		File target = new File(to);
606 		if (target.exists()) {
607 			LogLog.debug("deleting existing target file: " + target);
608 			target.delete();
609 		}
610 
611 		File file = new File(from);
612 		file.renameTo(target);
613 		LogLog.debug(from +" -> "+ to);
614 	}
615 
616 	/** Delete's the specified file if it exists */
deleteFile(String fileName)617 	protected static void deleteFile(String fileName) {
618 		File file = new File(fileName);
619 		if (file.exists()) {
620 		   file.delete();
621 		}
622 	}
623 
624 	/**
625 	 Implements roll overs base on file size.
626 
627 	 <p>If the maximum number of size based backups is reached
628 	 (<code>curSizeRollBackups == maxSizeRollBackups</code) then the oldest
629 	 file is deleted -- it's index determined by the sign of countDirection.<br>
630 	 If <code>countDirection</code> < 0, then files
631 	 {<code>File.1</code>, ..., <code>File.curSizeRollBackups -1</code>}
632 	 are renamed to {<code>File.2</code>, ...,
633 	 <code>File.curSizeRollBackups</code>}.	 Moreover, <code>File</code> is
634 	 renamed <code>File.1</code> and closed.<br>
635 
636 	 A new file is created to receive further log output.
637 
638 	 <p>If <code>maxSizeRollBackups</code> is equal to zero, then the
639 	 <code>File</code> is truncated with no backup files created.
640 
641 	 <p>If <code>maxSizeRollBackups</code> < 0, then <code>File</code> is
642 	 renamed if needed and no files are deleted.
643 	*/
644 
645 	// synchronization not necessary since doAppend is alreasy synched
rollOverSize()646 	protected void rollOverSize() {
647 		File file;
648 
649 		this.closeFile(); // keep windows happy.
650 
651 		LogLog.debug("rolling over count=" + ((CountingQuietWriter) qw).getCount());
652 		LogLog.debug("maxSizeRollBackups = " + maxSizeRollBackups);
653 		LogLog.debug("curSizeRollBackups = " + curSizeRollBackups);
654 		LogLog.debug("countDirection = " + countDirection);
655 
656 		// If maxBackups <= 0, then there is no file renaming to be done.
657 		if (maxSizeRollBackups != 0) {
658 
659 			if (countDirection < 0) {
660 				// Delete the oldest file, to keep Windows happy.
661 				if (curSizeRollBackups == maxSizeRollBackups) {
662 				    deleteFile(fileName + '.' + maxSizeRollBackups);
663 					curSizeRollBackups--;
664 				}
665 
666 				// Map {(maxBackupIndex - 1), ..., 2, 1} to {maxBackupIndex, ..., 3, 2}
667 				for (int i = curSizeRollBackups; i >= 1; i--) {
668 					rollFile((fileName + "." + i), (fileName + '.' + (i + 1)));
669 				}
670 
671 				curSizeRollBackups++;
672 				// Rename fileName to fileName.1
673 				rollFile(fileName, fileName + ".1");
674 
675 			} //REMOVE This code branching for Alexander Cerna's request
676 			else if (countDirection == 0) {
677 				//rollFile based on date pattern
678 				curSizeRollBackups++;
679 				now.setTime(System.currentTimeMillis());
680 				scheduledFilename = fileName + sdf.format(now);
681 				rollFile(fileName, scheduledFilename);
682 			}
683 			else { //countDirection > 0
684 				if (curSizeRollBackups >= maxSizeRollBackups && maxSizeRollBackups > 0) {
685 					//delete the first and keep counting up.
686 					int oldestFileIndex = curSizeRollBackups - maxSizeRollBackups + 1;
687 					deleteFile(fileName + '.' + oldestFileIndex);
688 				}
689 
690 				if (staticLogFileName) {
691 					curSizeRollBackups++;
692 					rollFile(fileName, fileName + '.' + curSizeRollBackups);
693 				}
694 			}
695 		}
696 
697 		try {
698 			// This will also close the file. This is OK since multiple
699 			// close operations are safe.
700 			this.setFile(baseFileName, false);
701 		}
702 		catch(IOException e) {
703 			LogLog.error("setFile("+fileName+", false) call failed.", e);
704 		}
705 	}
706 
707 }