001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.log4j; 019 020import org.apache.log4j.spi.Filter; 021import org.apache.log4j.spi.ErrorHandler; 022import org.apache.log4j.spi.OptionHandler; 023import org.apache.log4j.spi.LoggingEvent; 024import org.apache.log4j.helpers.OnlyOnceErrorHandler; 025import org.apache.log4j.helpers.LogLog; 026 027 028/** 029 * Abstract superclass of the other appenders in the package. 030 * 031 * This class provides the code for common functionality, such as 032 * support for threshold filtering and support for general filters. 033 * 034 * @since 0.8.1 035 * @author Ceki Gülcü 036 * */ 037public abstract class AppenderSkeleton implements Appender, OptionHandler { 038 039 /** The layout variable does not need to be set if the appender 040 implementation has its own layout. */ 041 protected Layout layout; 042 043 /** Appenders are named. */ 044 protected String name; 045 046 /** 047 There is no level threshold filtering by default. */ 048 protected Priority threshold; 049 050 /** 051 It is assumed and enforced that errorHandler is never null. 052 */ 053 protected ErrorHandler errorHandler = new OnlyOnceErrorHandler(); 054 055 /** The first filter in the filter chain. Set to <code>null</code> 056 initially. */ 057 protected Filter headFilter; 058 /** The last filter in the filter chain. */ 059 protected Filter tailFilter; 060 061 /** 062 Is this appender closed? 063 */ 064 protected boolean closed = false; 065 066 /** 067 * Create new instance. 068 */ 069 public AppenderSkeleton() { 070 super(); 071 } 072 073 /** 074 * Create new instance. 075 * Provided for compatibility with log4j 1.3. 076 * 077 * @param isActive true if appender is ready for use upon construction. 078 * Not used in log4j 1.2.x. 079 * @since 1.2.15 080 */ 081 protected AppenderSkeleton(final boolean isActive) { 082 super(); 083 } 084 085 086 087 /** 088 Derived appenders should override this method if option structure 089 requires it. */ 090 public 091 void activateOptions() { 092 } 093 094 095 /** 096 Add a filter to end of the filter list. 097 098 @since 0.9.0 099 */ 100 public 101 void addFilter(Filter newFilter) { 102 if(headFilter == null) { 103 headFilter = tailFilter = newFilter; 104 } else { 105 tailFilter.setNext(newFilter); 106 tailFilter = newFilter; 107 } 108 } 109 110 /** 111 Subclasses of <code>AppenderSkeleton</code> should implement this 112 method to perform actual logging. See also {@link #doAppend 113 AppenderSkeleton.doAppend} method. 114 115 @since 0.9.0 116 */ 117 abstract 118 protected 119 void append(LoggingEvent event); 120 121 122 /** 123 Clear the filters chain. 124 125 @since 0.9.0 */ 126 public 127 void clearFilters() { 128 headFilter = tailFilter = null; 129 } 130 131 /** 132 Finalize this appender by calling the derived class' 133 <code>close</code> method. 134 135 @since 0.8.4 */ 136 public 137 void finalize() { 138 // An appender might be closed then garbage collected. There is no 139 // point in closing twice. 140 if(this.closed) 141 return; 142 143 LogLog.debug("Finalizing appender named ["+name+"]."); 144 close(); 145 } 146 147 148 /** 149 Return the currently set {@link ErrorHandler} for this 150 Appender. 151 152 @since 0.9.0 */ 153 public 154 ErrorHandler getErrorHandler() { 155 return this.errorHandler; 156 } 157 158 159 /** 160 Returns the head Filter. 161 162 @since 1.1 163 */ 164 public 165 Filter getFilter() { 166 return headFilter; 167 } 168 169 /** 170 Return the first filter in the filter chain for this 171 Appender. The return value may be <code>null</code> if no is 172 filter is set. 173 174 */ 175 public 176 final 177 Filter getFirstFilter() { 178 return headFilter; 179 } 180 181 /** 182 Returns the layout of this appender. The value may be null. 183 */ 184 public 185 Layout getLayout() { 186 return layout; 187 } 188 189 190 /** 191 Returns the name of this appender. 192 @return name, may be null. 193 */ 194 public 195 final 196 String getName() { 197 return this.name; 198 } 199 200 /** 201 Returns this appenders threshold level. See the {@link 202 #setThreshold} method for the meaning of this option. 203 204 @since 1.1 */ 205 public 206 Priority getThreshold() { 207 return threshold; 208 } 209 210 211 /** 212 Check whether the message level is below the appender's 213 threshold. If there is no threshold set, then the return value is 214 always <code>true</code>. 215 216 */ 217 public 218 boolean isAsSevereAsThreshold(Priority priority) { 219 return ((threshold == null) || priority.isGreaterOrEqual(threshold)); 220 } 221 222 223 /** 224 * This method performs threshold checks and invokes filters before 225 * delegating actual logging to the subclasses specific {@link 226 * AppenderSkeleton#append} method. 227 * */ 228 public 229 synchronized 230 void doAppend(LoggingEvent event) { 231 if(closed) { 232 LogLog.error("Attempted to append to closed appender named ["+name+"]."); 233 return; 234 } 235 236 if(!isAsSevereAsThreshold(event.getLevel())) { 237 return; 238 } 239 240 Filter f = this.headFilter; 241 242 FILTER_LOOP: 243 while(f != null) { 244 switch(f.decide(event)) { 245 case Filter.DENY: return; 246 case Filter.ACCEPT: break FILTER_LOOP; 247 case Filter.NEUTRAL: f = f.getNext(); 248 } 249 } 250 251 this.append(event); 252 } 253 254 /** 255 Set the {@link ErrorHandler} for this Appender. 256 @since 0.9.0 257 */ 258 public 259 synchronized 260 void setErrorHandler(ErrorHandler eh) { 261 if(eh == null) { 262 // We do not throw exception here since the cause is probably a 263 // bad config file. 264 LogLog.warn("You have tried to set a null error-handler."); 265 } else { 266 this.errorHandler = eh; 267 } 268 } 269 270 /** 271 Set the layout for this appender. Note that some appenders have 272 their own (fixed) layouts or do not use one. For example, the 273 {@link org.apache.log4j.net.SocketAppender} ignores the layout set 274 here. 275 */ 276 public 277 void setLayout(Layout layout) { 278 this.layout = layout; 279 } 280 281 282 /** 283 Set the name of this Appender. 284 */ 285 public 286 void setName(String name) { 287 this.name = name; 288 } 289 290 291 /** 292 Set the threshold level. All log events with lower level 293 than the threshold level are ignored by the appender. 294 295 <p>In configuration files this option is specified by setting the 296 value of the <b>Threshold</b> option to a level 297 string, such as "DEBUG", "INFO" and so on. 298 299 @since 0.8.3 */ 300 public 301 void setThreshold(Priority threshold) { 302 this.threshold = threshold; 303 } 304}