/* * (C) 2007-2012 Alibaba Group Holding Limited. * * Licensed 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. * Authors: * wuhua <wq163@163.com> , boyan <killme2008@gmail.com> */ package com.taobao.metamorphosis.client.extension.log4j; import java.io.BufferedOutputStream; import java.io.BufferedWriter; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import org.apache.log4j.Layout; import org.apache.log4j.helpers.LogLog; /** * * @author wuxin * @since 1.0, 2009-10-20 ����03:49:32 */ public class FileStreamAppender extends StreamAppender { /** * 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 FileStreamAppender() { } /** * 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 FileStreamAppender(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 FileStreamAppender(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 FileStreamAppender(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 synchronized 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 synchronized 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 */ @Override public void activateOptions() { if (fileName != null) { try { setFile(fileName, fileAppend, bufferedIO, bufferSize); } catch (java.io.IOException e) { LogLog.error("setFile(" + fileName + "," + fileAppend + ") call failed.", e); } } 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.out != null) { try { this.out.close(); } catch (java.io.IOException e) { // Exceptionally, it does not make sense to delegate to an // ErrorHandler. Since a closed appender is basically dead. LogLog.error("Could not close " + out, 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; } } if (bufferedIO) { this.out = new BufferedOutputStream(ostream, bufferSize); } else { this.out = ostream; } this.fileName = fileName; this.fileAppend = append; this.bufferedIO = bufferedIO; this.bufferSize = bufferSize; writeHeader(); LogLog.debug("setFile ended"); } /** * Close any previously opened file and call the parent's <code>reset</code> * . */ @Override protected void reset() { closeFile(); this.fileName = null; super.reset(); } }