001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.commons.io.log.log4j.appender; 020 021import java.io.BufferedWriter; 022import java.io.IOException; 023import java.io.Writer; 024import java.nio.charset.Charset; 025 026import lucee.commons.io.res.Resource; 027import lucee.commons.io.retirement.RetireListener; 028import lucee.commons.io.retirement.RetireOutputStream; 029 030import org.apache.log4j.Layout; 031import org.apache.log4j.RollingFileAppender; 032import org.apache.log4j.WriterAppender; 033import org.apache.log4j.helpers.LogLog; 034import org.apache.log4j.helpers.QuietWriter; 035 036public class ResourceAppender extends WriterAppender implements AppenderState { 037 038 private static final int DEFAULT_BUFFER_SIZE = 8*1024; 039 040/** Controls file truncatation. The default value for this variable 041 * is <code>true</code>, meaning that by default a 042 * <code>FileAppender</code> will append to an existing file and not 043 * truncate it. 044 * 045 * <p>This option is meaningful only if the FileAppender opens the 046 * file. 047 */ 048 protected final boolean fileAppend; 049 050 /** 051 The name of the log file. */ 052 protected final Resource res; 053 054 /** 055 Do we do bufferedIO? */ 056 protected final boolean bufferedIO; 057 058 /** 059 * Determines the size of IO buffer be. Default is 8K. 060 */ 061 protected final int bufferSize; 062 063 private final int timeout; 064 065private final RetireListener listener; 066 067 /** 068 Instantiate a FileAppender and open the file designated by 069 <code>filename</code>. The opened filename will become the output 070 destination for this appender. 071 072 <p>The file will be appended to. */ 073 public ResourceAppender(Layout layout, Resource res,Charset charset,RetireListener listener) throws IOException { 074 this(layout, res,charset, true,false,60/* a minute */,DEFAULT_BUFFER_SIZE,listener); 075 } 076 077 078 /** 079 Instantiate a FileAppender and open the file designated by 080 <code>filename</code>. The opened filename will become the output 081 destination for this appender. 082 083 <p>If the <code>append</code> parameter is true, the file will be 084 appended to. Otherwise, the file designated by 085 <code>filename</code> will be truncated before being opened. 086 */ 087 public ResourceAppender(Layout layout, Resource res,Charset charset, boolean append,RetireListener listener) throws IOException { 088 this(layout,res,charset,append,false,60/* a minute */,DEFAULT_BUFFER_SIZE,listener); 089 } 090 091 092 public ResourceAppender(Layout layout, Resource res,Charset charset, boolean append, int timeout,RetireListener listener) throws IOException { 093 this(layout,res,charset,append,false,timeout,DEFAULT_BUFFER_SIZE,listener); 094 } 095 096 /** 097 Instantiate a <code>FileAppender</code> and open the file 098 designated by <code>filename</code>. The opened filename will 099 become the output destination for this appender. 100 101 <p>If the <code>append</code> parameter is true, the file will be 102 appended to. Otherwise, the file designated by 103 <code>filename</code> will be truncated before being opened. 104 105 <p>If the <code>bufferedIO</code> parameter is <code>true</code>, 106 then buffered IO will be used to write to the output file. 107 108 */ 109 public ResourceAppender(Layout layout, Resource res,Charset charset, boolean append, boolean bufferedIO, 110 int timeout,int bufferSize,RetireListener listener) throws IOException { 111 this.layout = layout; 112 this.bufferedIO=bufferedIO; 113 this.bufferSize=bufferSize; 114 this.timeout=timeout; 115 this.fileAppend=append; 116 this.res=res; 117 this.listener=listener; 118 setEncoding(charset.name()); 119 this.setFile(append); 120 } 121 122 /** 123 Returns the value of the <b>Append</b> option. 124 */ 125 public boolean getAppend() { 126 return fileAppend; 127 } 128 129 130 /** Returns the value of the <b>File</b> option. */ 131 public Resource getResource() { 132 return res; 133 } 134 135 /** 136 If the value of <b>File</b> is not <code>null</code>, then {@link 137 #setFile} is called with the values of <b>File</b> and 138 <b>Append</b> properties. 139 140 @since 0.8.1 */ 141 142 143 /** 144 Closes the previously opened file. 145 */ 146 protected 147 void closeFile() { 148 if(this.qw != null) { 149 try { 150 this.qw.close(); 151 } 152 catch(java.io.IOException e) { 153 // Exceptionally, it does not make sense to delegate to an 154 // ErrorHandler. Since a closed appender is basically dead. 155 LogLog.error("Could not close " + qw, e); 156 } 157 } 158 } 159 160 /** 161 Get the value of the <b>BufferedIO</b> option. 162 163 <p>BufferedIO will significatnly increase performance on heavily 164 loaded systems. 165 166 */ 167 public 168 boolean getBufferedIO() { 169 return this.bufferedIO; 170 } 171 172 173 /** 174 Get the size of the IO buffer. 175 */ 176 public 177 int getBufferSize() { 178 return this.bufferSize; 179 } 180 181 182 183 184 /** 185 <p>Sets and <i>opens</i> the file where the log output will 186 go. The specified file must be writable. 187 188 <p>If there was already an opened file, then the previous file 189 is closed first. 190 191 <p><b>Do not use this method directly. To configure a FileAppender 192 or one of its subclasses, set its properties one by one and then 193 call activateOptions.</b> 194 195 @param fileName The path to the log file. 196 @param append If true will append to fileName. Otherwise will 197 truncate fileName. */ 198 protected synchronized void setFile(boolean append) throws IOException { 199 LogLog.debug("setFile called: "+res+", "+append); 200 201 // It does not make sense to have immediate flush and bufferedIO. 202 if(bufferedIO) { 203 setImmediateFlush(false); 204 } 205 206 reset(); 207 Resource parent = res.getParentResource(); 208 if(!parent.exists()) parent.createDirectory(true); 209 boolean writeHeader = !append || res.length()==0;// this must happen before we open the stream 210 Writer fw = createWriter(new RetireOutputStream(res, append, timeout,listener)); 211 if(bufferedIO) { 212 fw = new BufferedWriter(fw, bufferSize); 213 } 214 this.setQWForFiles(fw); 215 if(writeHeader) writeHeader(); 216 LogLog.debug("setFile ended"); 217 } 218 219 220 /** 221 Sets the quiet writer being used. 222 223 This method is overriden by {@link RollingFileAppender}. 224 */ 225 protected void setQWForFiles(Writer writer) { 226 this.qw = new QuietWriter(writer, errorHandler); 227 } 228 229 230 /** 231 Close any previously opened file and call the parent's 232 <code>reset</code>. */ 233 protected void reset() { 234 closeFile(); 235 super.reset(); 236 } 237 238@Override 239public boolean isClosed() { 240 return closed; 241} 242}