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