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