FileAppender.java
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 package org.apache.log4j; 18 19 import java.io.IOException; 20 import java.io.Writer; 21 import java.io.FileOutputStream; 22 import java.io.BufferedWriter; 23 import java.io.FileNotFoundException; 24 import java.io.File; 25 26 import org.apache.log4j.spi.ErrorCode; 27 import org.apache.log4j.helpers.QuietWriter; 28 import org.apache.log4j.helpers.LogLog; 29 30 // Contibutors: Jens Uwe Pipka <jens.pipka@gmx.de> 31 // Ben Sandee 32 33 /** 34 * FileAppender appends log events to a file. 35 * 36 * <p>Support for <code>java.io.Writer</code> and console appending 37 * has been deprecated and then removed. See the replacement 38 * solutions: {@link WriterAppender} and {@link ConsoleAppender}. 39 * 40 * @author Ceki Gülcü 41 * */ 42 public class FileAppender extends WriterAppender { 43 44 /** Controls file truncatation. The default value for this variable 45 * is <code>true</code>, meaning that by default a 46 * <code>FileAppender</code> will append to an existing file and not 47 * truncate it. 48 * 49 * <p>This option is meaningful only if the FileAppender opens the 50 * file. 51 */ 52 protected boolean fileAppend = true; 53 54 /** 55 The name of the log file. */ 56 protected String fileName = null; 57 58 /** 59 Do we do bufferedIO? */ 60 protected boolean bufferedIO = false; 61 62 /** 63 * Determines the size of IO buffer be. Default is 8K. 64 */ 65 protected int bufferSize = 8*1024; 66 67 68 /** 69 The default constructor does not do anything. 70 */ 71 public 72 FileAppender() { 73 } 74 75 /** 76 Instantiate a <code>FileAppender</code> and open the file 77 designated by <code>filename</code>. The opened filename will 78 become the output destination for this appender. 79 80 <p>If the <code>append</code> parameter is true, the file will be 81 appended to. Otherwise, the file designated by 82 <code>filename</code> will be truncated before being opened. 83 84 <p>If the <code>bufferedIO</code> parameter is <code>true</code>, 85 then buffered IO will be used to write to the output file. 86 87 */ 88 public 89 FileAppender(Layout layout, String filename, boolean append, boolean bufferedIO, 90 int bufferSize) throws IOException { 91 this.layout = layout; 92 this.setFile(filename, append, bufferedIO, bufferSize); 93 } 94 95 /** 96 Instantiate a FileAppender and open the file designated by 97 <code>filename</code>. The opened filename will become the output 98 destination for this appender. 99 100 <p>If the <code>append</code> parameter is true, the file will be 101 appended to. Otherwise, the file designated by 102 <code>filename</code> will be truncated before being opened. 103 */ 104 public 105 FileAppender(Layout layout, String filename, boolean append) 106 throws IOException { 107 this.layout = layout; 108 this.setFile(filename, append, false, bufferSize); 109 } 110 111 /** 112 Instantiate a FileAppender and open the file designated by 113 <code>filename</code>. The opened filename will become the output 114 destination for this appender. 115 116 <p>The file will be appended to. */ 117 public 118 FileAppender(Layout layout, String filename) throws IOException { 119 this(layout, filename, true); 120 } 121 122 /** 123 The <b>File</b> property takes a string value which should be the 124 name of the file to append to. 125 126 <p><font color="#DD0044"><b>Note that the special values 127 "System.out" or "System.err" are no longer honored.</b></font> 128 129 <p>Note: Actual opening of the file is made when {@link 130 #activateOptions} is called, not when the options are set. */ 131 public void setFile(String file) { 132 // Trim spaces from both ends. The users probably does not want 133 // trailing spaces in file names. 134 String val = file.trim(); 135 fileName = val; 136 } 137 138 /** 139 Returns the value of the <b>Append</b> option. 140 */ 141 public 142 boolean getAppend() { 143 return fileAppend; 144 } 145 146 147 /** Returns the value of the <b>File</b> option. */ 148 public 149 String getFile() { 150 return fileName; 151 } 152 153 /** 154 If the value of <b>File</b> is not <code>null</code>, then {@link 155 #setFile} is called with the values of <b>File</b> and 156 <b>Append</b> properties. 157 158 @since 0.8.1 */ 159 public 160 void activateOptions() { 161 if(fileName != null) { 162 try { 163 setFile(fileName, fileAppend, bufferedIO, bufferSize); 164 } 165 catch(java.io.IOException e) { 166 errorHandler.error("setFile("+fileName+","+fileAppend+") call failed.", 167 e, ErrorCode.FILE_OPEN_FAILURE); 168 } 169 } else { 170 //LogLog.error("File option not set for appender ["+name+"]."); 171 LogLog.warn("File option not set for appender ["+name+"]."); 172 LogLog.warn("Are you using FileAppender instead of ConsoleAppender?"); 173 } 174 } 175 176 /** 177 Closes the previously opened file. 178 */ 179 protected 180 void closeFile() { 181 if(this.qw != null) { 182 try { 183 this.qw.close(); 184 } 185 catch(java.io.IOException e) { 186 // Exceptionally, it does not make sense to delegate to an 187 // ErrorHandler. Since a closed appender is basically dead. 188 LogLog.error("Could not close " + qw, e); 189 } 190 } 191 } 192 193 /** 194 Get the value of the <b>BufferedIO</b> option. 195 196 <p>BufferedIO will significatnly increase performance on heavily 197 loaded systems. 198 199 */ 200 public 201 boolean getBufferedIO() { 202 return this.bufferedIO; 203 } 204 205 206 /** 207 Get the size of the IO buffer. 208 */ 209 public 210 int getBufferSize() { 211 return this.bufferSize; 212 } 213 214 215 216 /** 217 The <b>Append</b> option takes a boolean value. It is set to 218 <code>true</code> by default. If true, then <code>File</code> 219 will be opened in append mode by {@link #setFile setFile} (see 220 above). Otherwise, {@link #setFile setFile} will open 221 <code>File</code> in truncate mode. 222 223 <p>Note: Actual opening of the file is made when {@link 224 #activateOptions} is called, not when the options are set. 225 */ 226 public 227 void setAppend(boolean flag) { 228 fileAppend = flag; 229 } 230 231 /** 232 The <b>BufferedIO</b> option takes a boolean value. It is set to 233 <code>false</code> by default. If true, then <code>File</code> 234 will be opened and the resulting {@link java.io.Writer} wrapped 235 around a {@link BufferedWriter}. 236 237 BufferedIO will significatnly increase performance on heavily 238 loaded systems. 239 240 */ 241 public 242 void setBufferedIO(boolean bufferedIO) { 243 this.bufferedIO = bufferedIO; 244 if(bufferedIO) { 245 immediateFlush = false; 246 } 247 } 248 249 250 /** 251 Set the size of the IO buffer. 252 */ 253 public 254 void setBufferSize(int bufferSize) { 255 this.bufferSize = bufferSize; 256 } 257 258 /** 259 <p>Sets and <i>opens</i> the file where the log output will 260 go. The specified file must be writable. 261 262 <p>If there was already an opened file, then the previous file 263 is closed first. 264 265 <p><b>Do not use this method directly. To configure a FileAppender 266 or one of its subclasses, set its properties one by one and then 267 call activateOptions.</b> 268 269 @param fileName The path to the log file. 270 @param append If true will append to fileName. Otherwise will 271 truncate fileName. */ 272 public 273 synchronized 274 void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize) 275 throws IOException { 276 LogLog.debug("setFile called: "+fileName+", "+append); 277 278 // It does not make sense to have immediate flush and bufferedIO. 279 if(bufferedIO) { 280 setImmediateFlush(false); 281 } 282 283 reset(); 284 FileOutputStream ostream = null; 285 try { 286 // 287 // attempt to create file 288 // 289 ostream = new FileOutputStream(fileName, append); 290 } catch(FileNotFoundException ex) { 291 // 292 // if parent directory does not exist then 293 // attempt to create it and try to create file 294 // see bug 9150 295 // 296 String parentName = new File(fileName).getParent(); 297 if (parentName != null) { 298 File parentDir = new File(parentName); 299 if(!parentDir.exists() && parentDir.mkdirs()) { 300 ostream = new FileOutputStream(fileName, append); 301 } else { 302 throw ex; 303 } 304 } else { 305 throw ex; 306 } 307 } 308 Writer fw = createWriter(ostream); 309 if(bufferedIO) { 310 fw = new BufferedWriter(fw, bufferSize); 311 } 312 this.setQWForFiles(fw); 313 this.fileName = fileName; 314 this.fileAppend = append; 315 this.bufferedIO = bufferedIO; 316 this.bufferSize = bufferSize; 317 writeHeader(); 318 LogLog.debug("setFile ended"); 319 } 320 321 322 /** 323 Sets the quiet writer being used. 324 325 This method is overriden by {@link RollingFileAppender}. 326 */ 327 protected 328 void setQWForFiles(Writer writer) { 329 this.qw = new QuietWriter(writer, errorHandler); 330 } 331 332 333 /** 334 Close any previously opened file and call the parent's 335 <code>reset</code>. */ 336 protected 337 void reset() { 338 closeFile(); 339 this.fileName = null; 340 super.reset(); 341 } 342 } 343