View Javadoc
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  package org.apache.logging.log4j.core.config;
18  
19  import java.util.Objects;
20  
21  import org.apache.logging.log4j.Level;
22  import org.apache.logging.log4j.core.Appender;
23  import org.apache.logging.log4j.core.Filter;
24  import org.apache.logging.log4j.core.LogEvent;
25  import org.apache.logging.log4j.core.appender.AppenderLoggingException;
26  import org.apache.logging.log4j.core.filter.AbstractFilterable;
27  import org.apache.logging.log4j.core.filter.Filterable;
28  
29  /**
30   * Wraps an {@link Appender} with details an appender implementation shouldn't need to know about.
31   */
32  public class AppenderControl extends AbstractFilterable {
33      private static final long serialVersionUID = 1L;
34  
35      private final ThreadLocal<AppenderControl> recursive = new ThreadLocal<>();
36      private final Appender appender;
37      private final Level level;
38      private final int intLevel;
39      private final String appenderName;
40  
41      /**
42       * Constructor.
43       * @param appender The target Appender.
44       * @param level the Level to filter on.
45       * @param filter the Filter(s) to apply.
46       */
47      public AppenderControl(final Appender appender, final Level level, final Filter filter) {
48          super(filter);
49          this.appender = appender;
50          this.appenderName = appender.getName();
51          this.level = level;
52          this.intLevel = level == null ? Level.ALL.intLevel() : level.intLevel();
53          start();
54      }
55      
56      /**
57       * Returns the name the appender had when this AppenderControl was constructed.
58       * @return the appender name
59       */
60      public String getAppenderName() {
61          return appenderName;
62      }
63  
64      /**
65       * Returns the Appender.
66       * @return the Appender.
67       */
68      public Appender getAppender() {
69          return appender;
70      }
71  
72      /**
73       * Call the appender.
74       * @param event The event to process.
75       */
76      public void callAppender(final LogEvent event) {
77          if (shouldSkip(event)) {
78              return;
79          }
80          callAppenderPreventRecursion(event);
81      }
82  
83      private boolean shouldSkip(final LogEvent event) {
84          return isFilteredByAppenderControl(event) || isFilteredByLevel(event) || isRecursiveCall();
85      }
86  
87      private boolean isFilteredByAppenderControl(final LogEvent event) {
88          return getFilter() != null && Filter.Result.DENY == getFilter().filter(event);
89      }
90  
91      private boolean isFilteredByLevel(final LogEvent event) {
92          return level != null && intLevel < event.getLevel().intLevel();
93      }
94  
95      private boolean isRecursiveCall() {
96          if (recursive.get() != null) {
97              appenderErrorHandlerMessage("Recursive call to appender ");
98              return true;
99          }
100         return false;
101     }
102     
103     private String appenderErrorHandlerMessage(final String prefix) {
104         String result = createErrorMsg(prefix);
105         appender.getHandler().error(result);
106         return result;
107     }
108 
109     private void callAppenderPreventRecursion(final LogEvent event) {
110         try {
111             recursive.set(this);            
112             callAppender0(event);
113         } finally {
114             recursive.set(null);
115         }
116     }
117 
118     private void callAppender0(final LogEvent event) {
119         ensureAppenderStarted();
120         if (!isFilteredByAppender(event)) {
121             tryCallAppender(event);
122         }
123     }
124 
125     private void ensureAppenderStarted() {
126         if (!appender.isStarted()) {
127             handleError("Attempted to append to non-started appender ");
128         }
129     }
130 
131     private void handleError(final String prefix) {
132         final String msg = appenderErrorHandlerMessage(prefix);
133         if (!appender.ignoreExceptions()) {
134             throw new AppenderLoggingException(msg);
135         }
136     }
137 
138     private String createErrorMsg(final String prefix) {
139         return prefix + appender.getName();
140     }
141     
142     private boolean isFilteredByAppender(final LogEvent event) {
143         return appender instanceof Filterable && ((Filterable) appender).isFiltered(event);
144     }
145 
146     private void tryCallAppender(final LogEvent event) {
147         try {
148             appender.append(event);
149         } catch (final RuntimeException ex) {
150             handleAppenderError(ex);
151         } catch (final Exception ex) {
152             handleAppenderError(new AppenderLoggingException(ex));
153         }
154     }
155 
156     private void handleAppenderError(final RuntimeException ex) {
157         appender.getHandler().error(createErrorMsg("An exception occurred processing Appender "), ex);
158         if (!appender.ignoreExceptions()) {
159             throw ex;
160         }
161     }
162     
163     // AppenderControl is a helper object whose purpose is to make it
164     // easier for LoggerConfig to manage and invoke Appenders.
165     // LoggerConfig manages Appenders by their name. To facilitate this,
166     // two AppenderControl objects are considered equal if and only
167     // if they have the same appender name.
168     @Override
169     public boolean equals(final Object obj) {
170         if (obj == this) {
171             return true;
172         }
173         if (!(obj instanceof AppenderControl)) {
174             return false;
175         }
176         final AppenderControl other = (AppenderControl) obj;
177         return Objects.equals(appenderName, other.appenderName);
178     }
179     
180     @Override
181     public int hashCode() {
182         return appenderName.hashCode();
183     }
184 }