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}