001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 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 028/** 029 * Just a copy of the old log4j12 FileAppender. The ContainerLogAppender for YARN NodeManager needs 030 * this class but the log4j-1.2-api bridge does not provide it which causes the UTs in 031 * hbase-mapreduce module to fail if we start a separated MR cluster. 032 */ 033public class FileAppender extends WriterAppender { 034 035 /** 036 * Controls file truncatation. The default value for this variable is <code>true</code>, meaning 037 * that by default a <code>FileAppender</code> will append to an existing file and not truncate 038 * it. 039 * <p> 040 * This option is meaningful only if the FileAppender opens the file. 041 */ 042 protected boolean fileAppend = true; 043 044 /** 045 * The name of the log file. 046 */ 047 protected String fileName = null; 048 049 /** 050 * Do we do bufferedIO? 051 */ 052 protected boolean bufferedIO = false; 053 054 /** 055 * Determines the size of IO buffer be. Default is 8K. 056 */ 057 protected int bufferSize = 8 * 1024; 058 059 /** 060 * The default constructor does not do anything. 061 */ 062 public FileAppender() { 063 } 064 065 /** 066 * Instantiate a <code>FileAppender</code> and open the file designated by <code>fileName</code>. 067 * The opened filename will become the output destination for this appender. 068 * <p> 069 * If the <code>append</code> parameter is true, the file will be appended to. Otherwise, the file 070 * designated by <code>fileName</code> will be truncated before being opened. 071 * <p> 072 * If the <code>bufferedIO</code> parameter is <code>true</code>, then buffered IO will be used to 073 * write to the output file. 074 */ 075 public FileAppender(Layout layout, String fileName, boolean append, boolean bufferedIO, 076 int bufferSize) throws IOException { 077 this.layout = layout; 078 this.setFile(fileName, append, bufferedIO, bufferSize); 079 } 080 081 /** 082 * Instantiate a FileAppender and open the file designated by <code>fileName</code>. The opened 083 * filename will become the output destination for this appender. 084 * <p> 085 * If the <code>append</code> parameter is true, the file will be appended to. Otherwise, the file 086 * designated by <code>fileName</code> will be truncated before being opened. 087 */ 088 public FileAppender(Layout layout, String fileName, boolean append) throws IOException { 089 this.layout = layout; 090 this.setFile(fileName, append, false, bufferSize); 091 } 092 093 /** 094 * Instantiate a FileAppender and open the file designated by <code>filename</code>. The opened 095 * filename will become the output destination for this appender. 096 * <p> 097 * The file will be appended to. 098 */ 099 public FileAppender(Layout layout, String fileName) throws IOException { 100 this(layout, fileName, true); 101 } 102 103 /** 104 * The <b>File</b> property takes a string value which should be the name of the file to append 105 * to. 106 * <p> 107 * <font color="#DD0044"><b>Note that the special values "System.out" or "System.err" are no 108 * longer honored.</b></font> 109 * <p> 110 * Note: Actual opening of the file is made when {@link #activateOptions} is called, not when the 111 * options are set. 112 */ 113 public void setFile(String file) { 114 // Trim spaces from both ends. The users probably does not want 115 // trailing spaces in file names. 116 String val = file.trim(); 117 fileName = val; 118 } 119 120 /** 121 * Returns the value of the <b>Append</b> option. 122 */ 123 public boolean getAppend() { 124 return fileAppend; 125 } 126 127 /** Returns the value of the <b>File</b> option. */ 128 public String getFile() { 129 return fileName; 130 } 131 132 /** 133 * If the value of <b>File</b> is not <code>null</code>, then {@link #setFile} is called with the 134 * values of <b>File</b> and <b>Append</b> properties. 135 * @since 0.8.1 136 */ 137 @Override 138 public void activateOptions() { 139 if (fileName != null) { 140 try { 141 setFile(fileName, fileAppend, bufferedIO, bufferSize); 142 } catch (java.io.IOException e) { 143 errorHandler.error("setFile(" + fileName + "," + fileAppend + ") call failed.", e, 144 org.apache.log4j.spi.ErrorCode.FILE_OPEN_FAILURE); 145 } 146 } 147 } 148 149 /** 150 * Closes the previously opened file. 151 */ 152 protected void closeFile() { 153 if (this.qw != null) { 154 try { 155 this.qw.close(); 156 } catch (java.io.IOException e) { 157 if (e instanceof InterruptedIOException) { 158 Thread.currentThread().interrupt(); 159 } 160 // Exceptionally, it does not make sense to delegate to an 161 // ErrorHandler. Since a closed appender is basically dead. 162 } 163 } 164 } 165 166 /** 167 * Get the value of the <b>BufferedIO</b> option. 168 * <p> 169 * BufferedIO will significatnly increase performance on heavily loaded systems. 170 */ 171 public boolean getBufferedIO() { 172 return this.bufferedIO; 173 } 174 175 /** 176 * Get the size of the IO buffer. 177 */ 178 public int getBufferSize() { 179 return this.bufferSize; 180 } 181 182 /** 183 * The <b>Append</b> option takes a boolean value. It is set to <code>true</code> by default. If 184 * true, then <code>File</code> will be opened in append mode by {@link #setFile setFile} (see 185 * above). Otherwise, {@link #setFile setFile} will open <code>File</code> in truncate mode. 186 * <p> 187 * Note: Actual opening of the file is made when {@link #activateOptions} is called, not when the 188 * options are set. 189 */ 190 public void setAppend(boolean flag) { 191 fileAppend = flag; 192 } 193 194 /** 195 * The <b>BufferedIO</b> option takes a boolean value. It is set to <code>false</code> by default. 196 * If true, then <code>File</code> will be opened and the resulting {@link java.io.Writer} wrapped 197 * around a {@link BufferedWriter}. BufferedIO will significatnly increase performance on heavily 198 * loaded systems. 199 */ 200 public void setBufferedIO(boolean bufferedIO) { 201 this.bufferedIO = bufferedIO; 202 if (bufferedIO) { 203 immediateFlush = false; 204 } 205 } 206 207 /** 208 * Set the size of the IO buffer. 209 */ 210 public void setBufferSize(int bufferSize) { 211 this.bufferSize = bufferSize; 212 } 213 214 /** 215 * <p> 216 * Sets and <i>opens</i> the file where the log output will go. The specified file must be 217 * writable. 218 * <p> 219 * If there was already an opened file, then the previous file is closed first. 220 * <p> 221 * <b>Do not use this method directly. To configure a FileAppender or one of its subclasses, set 222 * its properties one by one and then call activateOptions.</b> 223 * @param fileName The path to the log file. 224 * @param append If true will append to fileName. Otherwise will truncate fileName. 225 */ 226 public synchronized void setFile(String fileName, boolean append, boolean bufferedIO, 227 int bufferSize) throws IOException { 228 229 // It does not make sense to have immediate flush and bufferedIO. 230 if (bufferedIO) { 231 setImmediateFlush(false); 232 } 233 234 reset(); 235 FileOutputStream ostream = null; 236 try { 237 // 238 // attempt to create file 239 // 240 ostream = new FileOutputStream(fileName, append); 241 } catch (FileNotFoundException ex) { 242 // 243 // if parent directory does not exist then 244 // attempt to create it and try to create file 245 // see bug 9150 246 // 247 String parentName = new File(fileName).getParent(); 248 if (parentName != null) { 249 File parentDir = new File(parentName); 250 if (!parentDir.exists() && parentDir.mkdirs()) { 251 ostream = new FileOutputStream(fileName, append); 252 } else { 253 throw ex; 254 } 255 } else { 256 throw ex; 257 } 258 } 259 Writer fw = createWriter(ostream); 260 if (bufferedIO) { 261 fw = new BufferedWriter(fw, bufferSize); 262 } 263 this.setQWForFiles(fw); 264 this.fileName = fileName; 265 this.fileAppend = append; 266 this.bufferedIO = bufferedIO; 267 this.bufferSize = bufferSize; 268 writeHeader(); 269 } 270 271 /** 272 * Sets the quiet writer being used. This method is overriden by {@code RollingFileAppender}. 273 */ 274 protected void setQWForFiles(Writer writer) { 275 this.qw = new org.apache.log4j.helpers.QuietWriter(writer, errorHandler); 276 } 277 278 /** 279 * Close any previously opened file and call the parent's <code>reset</code>. 280 */ 281 @Override 282 protected void reset() { 283 closeFile(); 284 this.fileName = null; 285 super.reset(); 286 } 287}