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       * 
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      private boolean isFilteredByAppenderControl(final LogEvent event) {
92          return getFilter() != null && Filter.Result.DENY == getFilter().filter(event);
93      }
94  
95      private boolean isFilteredByLevel(final LogEvent event) {
96          return level != null && intLevel < event.getLevel().intLevel();
97      }
98  
99      private boolean isRecursiveCall() {
100         if (recursive.get() != null) {
101             appenderErrorHandlerMessage("Recursive call to appender ");
102             return true;
103         }
104         return false;
105     }
106 
107     private String appenderErrorHandlerMessage(final String prefix) {
108         String result = createErrorMsg(prefix);
109         appender.getHandler().error(result);
110         return result;
111     }
112 
113     private void callAppenderPreventRecursion(final LogEvent event) {
114         try {
115             recursive.set(this);
116             callAppender0(event);
117         } finally {
118             recursive.set(null);
119         }
120     }
121 
122     private void callAppender0(final LogEvent event) {
123         ensureAppenderStarted();
124         if (!isFilteredByAppender(event)) {
125             tryCallAppender(event);
126         }
127     }
128 
129     private void ensureAppenderStarted() {
130         if (!appender.isStarted()) {
131             handleError("Attempted to append to non-started appender ");
132         }
133     }
134 
135     private void handleError(final String prefix) {
136         final String msg = appenderErrorHandlerMessage(prefix);
137         if (!appender.ignoreExceptions()) {
138             throw new AppenderLoggingException(msg);
139         }
140     }
141 
142     private String createErrorMsg(final String prefix) {
143         return prefix + appender.getName();
144     }
145 
146     private boolean isFilteredByAppender(final LogEvent event) {
147         return appender instanceof Filterable && ((Filterable) appender).isFiltered(event);
148     }
149 
150     private void tryCallAppender(final LogEvent event) {
151         try {
152             appender.append(event);
153         } catch (final RuntimeException ex) {
154             handleAppenderError(ex);
155         } catch (final Exception ex) {
156             handleAppenderError(new AppenderLoggingException(ex));
157         }
158     }
159 
160     private void handleAppenderError(final RuntimeException ex) {
161         appender.getHandler().error(createErrorMsg("An exception occurred processing Appender "), ex);
162         if (!appender.ignoreExceptions()) {
163             throw ex;
164         }
165     }
166 
167     // AppenderControl is a helper object whose purpose is to make it
168     // easier for LoggerConfig to manage and invoke Appenders.
169     // LoggerConfig manages Appenders by their name. To facilitate this,
170     // two AppenderControl objects are considered equal if and only
171     // if they have the same appender name.
172     @Override
173     public boolean equals(final Object obj) {
174         if (obj == this) {
175             return true;
176         }
177         if (!(obj instanceof AppenderControl)) {
178             return false;
179         }
180         final AppenderControl other = (AppenderControl) obj;
181         return Objects.equals(appenderName, other.appenderName);
182     }
183 
184     @Override
185     public int hashCode() {
186         return appenderName.hashCode();
187     }
188 
189     @Override
190     public String toString() {
191         return super.toString() + "[appender=" + appender + ", appenderName=" + appenderName + ", level=" + level
192                 + ", intLevel=" + intLevel + ", recursive=" + recursive + ", filter=" + getFilter() + "]";
193     }
194 }