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