1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 19 20 package org.apache.log4j; 21 22 import java.io.IOException; 23 import java.io.Writer; 24 import java.io.File; 25 import java.io.InterruptedIOException; 26 27 import org.apache.log4j.helpers.OptionConverter; 28 import org.apache.log4j.helpers.LogLog; 29 import org.apache.log4j.helpers.CountingQuietWriter; 30 import org.apache.log4j.spi.LoggingEvent; 31 32 /** 33 RollingFileAppender extends FileAppender to backup the log files when 34 they reach a certain size. 35 36 The log4j extras companion includes alternatives which should be considered 37 for new deployments and which are discussed in the documentation 38 for org.apache.log4j.rolling.RollingFileAppender. 39 40 41 @author Heinz Richter 42 @author Ceki Gülcü 43 44 */ 45 public class RollingFileAppender extends FileAppender { 46 47 /** 48 The default maximum file size is 10MB. 49 */ 50 protected long maxFileSize = 10*1024*1024; 51 52 /** 53 There is one backup file by default. 54 */ 55 protected int maxBackupIndex = 1; 56 57 private long nextRollover = 0; 58 59 /** 60 The default constructor simply calls its {@link 61 FileAppender#FileAppender parents constructor}. */ 62 public 63 RollingFileAppender() { 64 super(); 65 } 66 67 /** 68 Instantiate a RollingFileAppender and open the file designated by 69 <code>filename</code>. The opened filename will become the ouput 70 destination for this appender. 71 72 <p>If the <code>append</code> parameter is true, the file will be 73 appended to. Otherwise, the file desginated by 74 <code>filename</code> will be truncated before being opened. 75 */ 76 public 77 RollingFileAppender(Layout layout, String filename, boolean append) 78 throws IOException { 79 super(layout, filename, append); 80 } 81 82 /** 83 Instantiate a FileAppender and open the file designated by 84 <code>filename</code>. The opened filename will become the output 85 destination for this appender. 86 87 <p>The file will be appended to. */ 88 public 89 RollingFileAppender(Layout layout, String filename) throws IOException { 90 super(layout, filename); 91 } 92 93 /** 94 Returns the value of the <b>MaxBackupIndex</b> option. 95 */ 96 public 97 int getMaxBackupIndex() { 98 return maxBackupIndex; 99 } 100 101 /** 102 Get the maximum size that the output file is allowed to reach 103 before being rolled over to backup files. 104 105 @since 1.1 106 */ 107 public 108 long getMaximumFileSize() { 109 return maxFileSize; 110 } 111 112 /** 113 Implements the usual roll over behaviour. 114 115 <p>If <code>MaxBackupIndex</code> is positive, then files 116 {<code>File.1</code>, ..., <code>File.MaxBackupIndex -1</code>} 117 are renamed to {<code>File.2</code>, ..., 118 <code>File.MaxBackupIndex</code>}. Moreover, <code>File</code> is 119 renamed <code>File.1</code> and closed. A new <code>File</code> is 120 created to receive further log output. 121 122 <p>If <code>MaxBackupIndex</code> is equal to zero, then the 123 <code>File</code> is truncated with no backup files created. 124 125 */ 126 public // synchronization not necessary since doAppend is alreasy synched 127 void rollOver() { 128 File target; 129 File file; 130 131 if (qw != null) { 132 long size = ((CountingQuietWriter) qw).getCount(); 133 LogLog.debug("rolling over count=" + size); 134 // if operation fails, do not roll again until 135 // maxFileSize more bytes are written 136 nextRollover = size + maxFileSize; 137 } 138 LogLog.debug("maxBackupIndex="+maxBackupIndex); 139 140 boolean renameSucceeded = true; 141 // If maxBackups <= 0, then there is no file renaming to be done. 142 if(maxBackupIndex > 0) { 143 // Delete the oldest file, to keep Windows happy. 144 file = new File(fileName + '.' + maxBackupIndex); 145 if (file.exists()) 146 renameSucceeded = file.delete(); 147 148 // Map {(maxBackupIndex - 1), ..., 2, 1} to {maxBackupIndex, ..., 3, 2} 149 for (int i = maxBackupIndex - 1; i >= 1 && renameSucceeded; i--) { 150 file = new File(fileName + "." + i); 151 if (file.exists()) { 152 target = new File(fileName + '.' + (i + 1)); 153 LogLog.debug("Renaming file " + file + " to " + target); 154 renameSucceeded = file.renameTo(target); 155 } 156 } 157 158 if(renameSucceeded) { 159 // Rename fileName to fileName.1 160 target = new File(fileName + "." + 1); 161 162 this.closeFile(); // keep windows happy. 163 164 file = new File(fileName); 165 LogLog.debug("Renaming file " + file + " to " + target); 166 renameSucceeded = file.renameTo(target); 167 // 168 // if file rename failed, reopen file with append = true 169 // 170 if (!renameSucceeded) { 171 try { 172 this.setFile(fileName, true, bufferedIO, bufferSize); 173 } 174 catch(IOException e) { 175 if (e instanceof InterruptedIOException) { 176 Thread.currentThread().interrupt(); 177 } 178 LogLog.error("setFile("+fileName+", true) call failed.", e); 179 } 180 } 181 } 182 } 183 184 // 185 // if all renames were successful, then 186 // 187 if (renameSucceeded) { 188 try { 189 // This will also close the file. This is OK since multiple 190 // close operations are safe. 191 this.setFile(fileName, false, bufferedIO, bufferSize); 192 nextRollover = 0; 193 } 194 catch(IOException e) { 195 if (e instanceof InterruptedIOException) { 196 Thread.currentThread().interrupt(); 197 } 198 LogLog.error("setFile("+fileName+", false) call failed.", e); 199 } 200 } 201 } 202 203 public 204 synchronized 205 void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize) 206 throws IOException { 207 super.setFile(fileName, append, this.bufferedIO, this.bufferSize); 208 if(append) { 209 File f = new File(fileName); 210 ((CountingQuietWriter) qw).setCount(f.length()); 211 } 212 } 213 214 215 /** 216 Set the maximum number of backup files to keep around. 217 218 <p>The <b>MaxBackupIndex</b> option determines how many backup 219 files are kept before the oldest is erased. This option takes 220 a positive integer value. If set to zero, then there will be no 221 backup files and the log file will be truncated when it reaches 222 <code>MaxFileSize</code>. 223 */ 224 public 225 void setMaxBackupIndex(int maxBackups) { 226 this.maxBackupIndex = maxBackups; 227 } 228 229 /** 230 Set the maximum size that the output file is allowed to reach 231 before being rolled over to backup files. 232 233 <p>This method is equivalent to {@link #setMaxFileSize} except 234 that it is required for differentiating the setter taking a 235 <code>long</code> argument from the setter taking a 236 <code>String</code> argument by the JavaBeans {@link 237 java.beans.Introspector Introspector}. 238 239 @see #setMaxFileSize(String) 240 */ 241 public 242 void setMaximumFileSize(long maxFileSize) { 243 this.maxFileSize = maxFileSize; 244 } 245 246 247 /** 248 Set the maximum size that the output file is allowed to reach 249 before being rolled over to backup files. 250 251 <p>In configuration files, the <b>MaxFileSize</b> option takes an 252 long integer in the range 0 - 2^63. You can specify the value 253 with the suffixes "KB", "MB" or "GB" so that the integer is 254 interpreted being expressed respectively in kilobytes, megabytes 255 or gigabytes. For example, the value "10KB" will be interpreted 256 as 10240. 257 */ 258 public 259 void setMaxFileSize(String value) { 260 maxFileSize = OptionConverter.toFileSize(value, maxFileSize + 1); 261 } 262 263 protected 264 void setQWForFiles(Writer writer) { 265 this.qw = new CountingQuietWriter(writer, errorHandler); 266 } 267 268 /** 269 This method differentiates RollingFileAppender from its super 270 class. 271 272 @since 0.9.0 273 */ 274 protected 275 void subAppend(LoggingEvent event) { 276 super.subAppend(event); 277 if(fileName != null && qw != null) { 278 long size = ((CountingQuietWriter) qw).getCount(); 279 if (size >= maxFileSize && size >= nextRollover) { 280 rollOver(); 281 } 282 } 283 } 284 }