/** * * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see <http://www.gnu.org/licenses/>. * **/ package lucee.commons.io.log.log4j.appender; import java.io.BufferedWriter; import java.io.IOException; import java.io.Writer; import java.nio.charset.Charset; import lucee.commons.io.res.Resource; import lucee.commons.io.retirement.RetireListener; import lucee.commons.io.retirement.RetireOutputStream; import org.apache.log4j.Layout; import org.apache.log4j.RollingFileAppender; import org.apache.log4j.WriterAppender; import org.apache.log4j.helpers.LogLog; import org.apache.log4j.helpers.QuietWriter; public class ResourceAppender extends WriterAppender implements AppenderState { private static final int DEFAULT_BUFFER_SIZE = 8*1024; /** 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 final boolean fileAppend; /** The name of the log file. */ protected final Resource res; /** Do we do bufferedIO? */ protected final boolean bufferedIO; /** * Determines the size of IO buffer be. Default is 8K. */ protected final int bufferSize; private final int timeout; private final RetireListener listener; /** 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 ResourceAppender(Layout layout, Resource res,Charset charset,RetireListener listener) throws IOException { this(layout, res,charset, true,false,60/* a minute */,DEFAULT_BUFFER_SIZE,listener); } /** 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 ResourceAppender(Layout layout, Resource res,Charset charset, boolean append,RetireListener listener) throws IOException { this(layout,res,charset,append,false,60/* a minute */,DEFAULT_BUFFER_SIZE,listener); } public ResourceAppender(Layout layout, Resource res,Charset charset, boolean append, int timeout,RetireListener listener) throws IOException { this(layout,res,charset,append,false,timeout,DEFAULT_BUFFER_SIZE,listener); } /** 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 ResourceAppender(Layout layout, Resource res,Charset charset, boolean append, boolean bufferedIO, int timeout,int bufferSize,RetireListener listener) throws IOException { this.layout = layout; this.bufferedIO=bufferedIO; this.bufferSize=bufferSize; this.timeout=timeout; this.fileAppend=append; this.res=res; this.listener=listener; setEncoding(charset.name()); this.setFile(append); } /** Returns the value of the <b>Append</b> option. */ public boolean getAppend() { return fileAppend; } /** Returns the value of the <b>File</b> option. */ public Resource getResource() { return res; } /** 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 */ /** Closes the previously opened file. */ protected void closeFile() { if(this.qw != null) { try { this.qw.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 " + 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; } /** <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. */ protected synchronized void setFile(boolean append) throws IOException { LogLog.debug("setFile called: "+res+", "+append); // It does not make sense to have immediate flush and bufferedIO. if(bufferedIO) { setImmediateFlush(false); } reset(); Resource parent = res.getParentResource(); if(!parent.exists()) parent.createDirectory(true); boolean writeHeader = !append || res.length()==0;// this must happen before we open the stream Writer fw = createWriter(new RetireOutputStream(res, append, timeout,listener)); if(bufferedIO) { fw = new BufferedWriter(fw, bufferSize); } this.setQWForFiles(fw); if(writeHeader) 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>. */ @Override protected void reset() { closeFile(); super.reset(); } @Override public boolean isClosed() { return closed; } }