--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.io.Writer;
+
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.helpers.QuietWriter;
+import org.apache.log4j.spi.ErrorCode;
+
+// Contibutors: Jens Uwe Pipka <jens.pipka@gmx.de>
+// Ben Sandee
+
+/**
+ * FileAppender appends log events to a file.
+ *
+ * <p>Support for <code>java.io.Writer</code> and console appending
+ * has been deprecated and then removed. See the replacement
+ * solutions: {@link WriterAppender} and {@link ConsoleAppender}.
+ *
+ * @author Ceki Gülcü
+ * */
+public class FileAppender extends WriterAppender {
+
+ /** Controls file truncatation. The default value for this variable
+ * is <code>true</code>, meaning that by default a
+ * <code>FileAppender</code> will append to an existing file and not
+ * truncate it.
+ *
+ * <p>This option is meaningful only if the FileAppender opens the
+ * file.
+ */
+ protected boolean fileAppend = true;
+
+ /**
+ The name of the log file. */
+ protected String fileName = null;
+
+ /**
+ Do we do bufferedIO? */
+ protected boolean bufferedIO = false;
+
+ /**
+ * Determines the size of IO buffer be. Default is 8K.
+ */
+ protected int bufferSize = 8*1024;
+
+
+ /**
+ The default constructor does not do anything.
+ */
+ public
+ FileAppender() {
+ }
+
+ /**
+ Instantiate a <code>FileAppender</code> and open the file
+ designated by <code>filename</code>. The opened filename will
+ become the output destination for this appender.
+
+ <p>If the <code>append</code> parameter is true, the file will be
+ appended to. Otherwise, the file designated by
+ <code>filename</code> will be truncated before being opened.
+
+ <p>If the <code>bufferedIO</code> parameter is <code>true</code>,
+ then buffered IO will be used to write to the output file.
+
+ */
+ public
+ FileAppender(Layout layout, String filename, boolean append, boolean bufferedIO,
+ int bufferSize) throws IOException {
+ this.layout = layout;
+ this.setFile(filename, append, bufferedIO, bufferSize);
+ }
+
+ /**
+ Instantiate a FileAppender and open the file designated by
+ <code>filename</code>. The opened filename will become the output
+ destination for this appender.
+
+ <p>If the <code>append</code> parameter is true, the file will be
+ appended to. Otherwise, the file designated by
+ <code>filename</code> will be truncated before being opened.
+ */
+ public
+ FileAppender(Layout layout, String filename, boolean append)
+ throws IOException {
+ this.layout = layout;
+ this.setFile(filename, append, false, bufferSize);
+ }
+
+ /**
+ Instantiate a FileAppender and open the file designated by
+ <code>filename</code>. The opened filename will become the output
+ destination for this appender.
+
+ <p>The file will be appended to. */
+ public
+ FileAppender(Layout layout, String filename) throws IOException {
+ this(layout, filename, true);
+ }
+
+ /**
+ The <b>File</b> property takes a string value which should be the
+ name of the file to append to.
+
+ <p><font color="#DD0044"><b>Note that the special values
+ "System.out" or "System.err" are no longer honored.</b></font>
+
+ <p>Note: Actual opening of the file is made when {@link
+ #activateOptions} is called, not when the options are set. */
+ public void setFile(String file) {
+ // Trim spaces from both ends. The users probably does not want
+ // trailing spaces in file names.
+ String val = file.trim();
+ fileName = val;
+ }
+
+ /**
+ Returns the value of the <b>Append</b> option.
+ */
+ public
+ boolean getAppend() {
+ return fileAppend;
+ }
+
+
+ /** Returns the value of the <b>File</b> option. */
+ public
+ String getFile() {
+ return fileName;
+ }
+
+ /**
+ If the value of <b>File</b> is not <code>null</code>, then {@link
+ #setFile} is called with the values of <b>File</b> and
+ <b>Append</b> properties.
+
+ @since 0.8.1 */
+ public
+ void activateOptions() {
+ if(fileName != null) {
+ try {
+ setFile(fileName, fileAppend, bufferedIO, bufferSize);
+ }
+ catch(java.io.IOException e) {
+ errorHandler.error("setFile("+fileName+","+fileAppend+") call failed.",
+ e, ErrorCode.FILE_OPEN_FAILURE);
+ }
+ } else {
+ //LogLog.error("File option not set for appender ["+name+"].");
+ LogLog.warn("File option not set for appender ["+name+"].");
+ LogLog.warn("Are you using FileAppender instead of ConsoleAppender?");
+ }
+ }
+
+ /**
+ Closes the previously opened file.
+ */
+ protected
+ void closeFile() {
+ if(this.qw != null) {
+ try {
+ this.qw.close();
+ }
+ catch(java.io.IOException e) {
+ if (e instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ // Exceptionally, it does not make sense to delegate to an
+ // ErrorHandler. Since a closed appender is basically dead.
+ LogLog.error("Could not close " + qw, e);
+ }
+ }
+ }
+
+ /**
+ Get the value of the <b>BufferedIO</b> option.
+
+ <p>BufferedIO will significatnly increase performance on heavily
+ loaded systems.
+
+ */
+ public
+ boolean getBufferedIO() {
+ return this.bufferedIO;
+ }
+
+
+ /**
+ Get the size of the IO buffer.
+ */
+ public
+ int getBufferSize() {
+ return this.bufferSize;
+ }
+
+
+
+ /**
+ The <b>Append</b> option takes a boolean value. It is set to
+ <code>true</code> by default. If true, then <code>File</code>
+ will be opened in append mode by {@link #setFile setFile} (see
+ above). Otherwise, {@link #setFile setFile} will open
+ <code>File</code> in truncate mode.
+
+ <p>Note: Actual opening of the file is made when {@link
+ #activateOptions} is called, not when the options are set.
+ */
+ public
+ void setAppend(boolean flag) {
+ fileAppend = flag;
+ }
+
+ /**
+ The <b>BufferedIO</b> option takes a boolean value. It is set to
+ <code>false</code> by default. If true, then <code>File</code>
+ will be opened and the resulting {@link java.io.Writer} wrapped
+ around a {@link BufferedWriter}.
+
+ BufferedIO will significatnly increase performance on heavily
+ loaded systems.
+
+ */
+ public
+ void setBufferedIO(boolean bufferedIO) {
+ this.bufferedIO = bufferedIO;
+ if(bufferedIO) {
+ immediateFlush = false;
+ }
+ }
+
+
+ /**
+ Set the size of the IO buffer.
+ */
+ public
+ void setBufferSize(int bufferSize) {
+ this.bufferSize = bufferSize;
+ }
+
+ /**
+ <p>Sets and <i>opens</i> the file where the log output will
+ go. The specified file must be writable.
+
+ <p>If there was already an opened file, then the previous file
+ is closed first.
+
+ <p><b>Do not use this method directly. To configure a FileAppender
+ or one of its subclasses, set its properties one by one and then
+ call activateOptions.</b>
+
+ @param fileName The path to the log file.
+ @param append If true will append to fileName. Otherwise will
+ truncate fileName. */
+ public
+ synchronized
+ void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize)
+ throws IOException {
+ LogLog.debug("setFile called: "+fileName+", "+append);
+
+ // It does not make sense to have immediate flush and bufferedIO.
+ if(bufferedIO) {
+ setImmediateFlush(false);
+ }
+
+ reset();
+ FileOutputStream ostream = null;
+ try {
+ //
+ // attempt to create file
+ //
+ ostream = new FileOutputStream(fileName, append);
+ } catch(FileNotFoundException ex) {
+ //
+ // if parent directory does not exist then
+ // attempt to create it and try to create file
+ // see bug 9150
+ //
+ String parentName = new File(fileName).getParent();
+ if (parentName != null) {
+ File parentDir = new File(parentName);
+ if(!parentDir.exists() && parentDir.mkdirs()) {
+ ostream = new FileOutputStream(fileName, append);
+ } else {
+ throw ex;
+ }
+ } else {
+ throw ex;
+ }
+ }
+ Writer fw = createWriter(ostream);
+ if(bufferedIO) {
+ fw = new BufferedWriter(fw, bufferSize);
+ }
+ this.setQWForFiles(fw);
+ this.fileName = fileName;
+ this.fileAppend = append;
+ this.bufferedIO = bufferedIO;
+ this.bufferSize = bufferSize;
+ writeHeader();
+ LogLog.debug("setFile ended");
+ }
+
+
+ /**
+ Sets the quiet writer being used.
+
+ This method is overriden by {@link RollingFileAppender}.
+ */
+ protected
+ void setQWForFiles(Writer writer) {
+ this.qw = new QuietWriter(writer, errorHandler);
+ }
+
+
+ /**
+ Close any previously opened file and call the parent's
+ <code>reset</code>. */
+ protected
+ void reset() {
+ closeFile();
+ this.fileName = null;
+ super.reset();
+ }
+}
+