1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.log4j;
19
20 import org.apache.log4j.spi.Filter;
21 import org.apache.log4j.spi.ErrorHandler;
22 import org.apache.log4j.spi.OptionHandler;
23 import org.apache.log4j.spi.LoggingEvent;
24 import org.apache.log4j.helpers.OnlyOnceErrorHandler;
25 import org.apache.log4j.helpers.LogLog;
26
27
28 /**
29 * Abstract superclass of the other appenders in the package.
30 *
31 * This class provides the code for common functionality, such as
32 * support for threshold filtering and support for general filters.
33 *
34 * @since 0.8.1
35 * @author Ceki Gülcü
36 * */
37 public abstract class AppenderSkeleton implements Appender, OptionHandler {
38
39 /** The layout variable does not need to be set if the appender
40 implementation has its own layout. */
41 protected Layout layout;
42
43 /** Appenders are named. */
44 protected String name;
45
46 /**
47 There is no level threshold filtering by default. */
48 protected Priority threshold;
49
50 /**
51 It is assumed and enforced that errorHandler is never null.
52 */
53 protected ErrorHandler errorHandler = new OnlyOnceErrorHandler();
54
55 /** The first filter in the filter chain. Set to <code>null</code>
56 initially. */
57 protected Filter headFilter;
58 /** The last filter in the filter chain. */
59 protected Filter tailFilter;
60
61 /**
62 Is this appender closed?
63 */
64 protected boolean closed = false;
65
66 /**
67 * Create new instance.
68 */
69 public AppenderSkeleton() {
70 super();
71 }
72
73 /**
74 * Create new instance.
75 * Provided for compatibility with log4j 1.3.
76 *
77 * @param isActive true if appender is ready for use upon construction.
78 * Not used in log4j 1.2.x.
79 * @since 1.2.15
80 */
81 protected AppenderSkeleton(final boolean isActive) {
82 super();
83 }
84
85
86
87 /**
88 Derived appenders should override this method if option structure
89 requires it. */
90 public
91 void activateOptions() {
92 }
93
94
95 /**
96 Add a filter to end of the filter list.
97
98 @since 0.9.0
99 */
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 }