001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.log4j; 019 020import java.io.BufferedWriter; 021import java.io.File; 022import java.io.FileNotFoundException; 023import java.io.FileOutputStream; 024import java.io.IOException; 025import java.io.InterruptedIOException; 026import java.io.Writer; 027 028import org.apache.log4j.helpers.LogLog; 029import org.apache.log4j.helpers.QuietWriter; 030import org.apache.log4j.spi.ErrorCode; 031 032// Contibutors: Jens Uwe Pipka <jens.pipka@gmx.de> 033// Ben Sandee 034 035/** 036 * FileAppender appends log events to a file. 037 * 038 * <p>Support for <code>java.io.Writer</code> and console appending 039 * has been deprecated and then removed. See the replacement 040 * solutions: {@link WriterAppender} and {@link ConsoleAppender}. 041 * 042 * @author Ceki Gülcü 043 * */ 044public class FileAppender extends WriterAppender { 045 046 /** Controls file truncatation. The default value for this variable 047 * is <code>true</code>, meaning that by default a 048 * <code>FileAppender</code> will append to an existing file and not 049 * truncate it. 050 * 051 * <p>This option is meaningful only if the FileAppender opens the 052 * file. 053 */ 054 protected boolean fileAppend = true; 055 056 /** 057 The name of the log file. */ 058 protected String fileName = null; 059 060 /** 061 Do we do bufferedIO? */ 062 protected boolean bufferedIO = false; 063 064 /** 065 * Determines the size of IO buffer be. Default is 8K. 066 */ 067 protected int bufferSize = 8*1024; 068 069 070 /** 071 The default constructor does not do anything. 072 */ 073 public 074 FileAppender() { 075 } 076 077 /** 078 Instantiate a <code>FileAppender</code> and open the file 079 designated by <code>filename</code>. The opened filename will 080 become the output destination for this appender. 081 082 <p>If the <code>append</code> parameter is true, the file will be 083 appended to. Otherwise, the file designated by 084 <code>filename</code> will be truncated before being opened. 085 086 <p>If the <code>bufferedIO</code> parameter is <code>true</code>, 087 then buffered IO will be used to write to the output file. 088 089 */ 090 public 091 FileAppender(Layout layout, String filename, boolean append, boolean bufferedIO, 092 int bufferSize) throws IOException { 093 this.layout = layout; 094 this.setFile(filename, append, bufferedIO, bufferSize); 095 } 096 097 /** 098 Instantiate a FileAppender and open the file designated by 099 <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