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.filter;
18  
19  import java.util.HashMap;
20  import java.util.Map;
21  import java.util.Objects;
22  
23  import org.apache.logging.log4j.Level;
24  import org.apache.logging.log4j.Marker;
25  import org.apache.logging.log4j.ThreadContext;
26  import org.apache.logging.log4j.core.Filter;
27  import org.apache.logging.log4j.core.LogEvent;
28  import org.apache.logging.log4j.core.Logger;
29  import org.apache.logging.log4j.core.config.Node;
30  import org.apache.logging.log4j.core.config.plugins.Plugin;
31  import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
32  import org.apache.logging.log4j.core.config.plugins.PluginElement;
33  import org.apache.logging.log4j.core.config.plugins.PluginFactory;
34  import org.apache.logging.log4j.core.ContextDataInjector;
35  import org.apache.logging.log4j.core.impl.ContextDataInjectorFactory;
36  import org.apache.logging.log4j.core.util.KeyValuePair;
37  import org.apache.logging.log4j.message.Message;
38  import org.apache.logging.log4j.util.PerformanceSensitive;
39  import org.apache.logging.log4j.util.ReadOnlyStringMap;
40  
41  /**
42   * Compares against a log level that is associated with a context value. By default the context is the
43   * {@link ThreadContext}, but users may {@linkplain ContextDataInjectorFactory configure} a custom
44   * {@link ContextDataInjector} which obtains context data from some other source.
45   */
46  @Plugin(name = "DynamicThresholdFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE, printObject = true)
47  @PerformanceSensitive("allocation")
48  public final class DynamicThresholdFilter extends AbstractFilter {
49  
50      /**
51       * Creates a DynamicThresholdFilter.
52       * @param key The name of the key to compare.
53       * @param pairs An array of value and Level pairs.
54       * @param defaultThreshold The default Level.
55       * @param onMatch The action to perform if a match occurs.
56       * @param onMismatch The action to perform if no match occurs.
57       * @return The DynamicThresholdFilter.
58       */
59      // TODO Consider refactoring to use AbstractFilter.AbstractFilterBuilder
60      @PluginFactory
61      public static DynamicThresholdFilter createFilter(
62              @PluginAttribute("key") final String key,
63              @PluginElement("Pairs") final KeyValuePair[] pairs,
64              @PluginAttribute("defaultThreshold") final Level defaultThreshold,
65              @PluginAttribute("onMatch") final Result onMatch,
66              @PluginAttribute("onMismatch") final Result onMismatch) {
67          final Map<String, Level> map = new HashMap<>();
68          for (final KeyValuePair pair : pairs) {
69              map.put(pair.getKey(), Level.toLevel(pair.getValue()));
70          }
71          final Level level = defaultThreshold == null ? Level.ERROR : defaultThreshold;
72          return new DynamicThresholdFilter(key, map, level, onMatch, onMismatch);
73      }
74  
75      private Level defaultThreshold = Level.ERROR;
76      private final String key;
77      private final ContextDataInjector injector = ContextDataInjectorFactory.createInjector();
78      private Map<String, Level> levelMap = new HashMap<>();
79  
80      private DynamicThresholdFilter(final String key, final Map<String, Level> pairs, final Level defaultLevel,
81                                     final Result onMatch, final Result onMismatch) {
82          super(onMatch, onMismatch);
83          Objects.requireNonNull(key, "key cannot be null");
84          this.key = key;
85          this.levelMap = pairs;
86          this.defaultThreshold = defaultLevel;
87      }
88  
89      @Override
90      public boolean equals(final Object obj) {
91          if (this == obj) {
92              return true;
93          }
94          if (!super.equalsImpl(obj)) {
95              return false;
96          }
97          if (getClass() != obj.getClass()) {
98              return false;
99          }
100         final DynamicThresholdFilter other = (DynamicThresholdFilter) obj;
101         if (defaultThreshold == null) {
102             if (other.defaultThreshold != null) {
103                 return false;
104             }
105         } else if (!defaultThreshold.equals(other.defaultThreshold)) {
106             return false;
107         }
108         if (key == null) {
109             if (other.key != null) {
110                 return false;
111             }
112         } else if (!key.equals(other.key)) {
113             return false;
114         }
115         if (levelMap == null) {
116             if (other.levelMap != null) {
117                 return false;
118             }
119         } else if (!levelMap.equals(other.levelMap)) {
120             return false;
121         }
122         return true;
123     }
124 
125     private Result filter(final Level level, final ReadOnlyStringMap contextMap) {
126         final String value = contextMap.getValue(key);
127         if (value != null) {
128             Level ctxLevel = levelMap.get(value);
129             if (ctxLevel == null) {
130                 ctxLevel = defaultThreshold;
131             }
132             return level.isMoreSpecificThan(ctxLevel) ? onMatch : onMismatch;
133         }
134         return Result.NEUTRAL;
135 
136     }
137 
138     @Override
139     public Result filter(final LogEvent event) {
140         return filter(event.getLevel(), event.getContextData());
141     }
142 
143     @Override
144     public Result filter(final Logger logger, final Level level, final Marker marker, final Message msg,
145                          final Throwable t) {
146         return filter(level, currentContextData());
147     }
148 
149     @Override
150     public Result filter(final Logger logger, final Level level, final Marker marker, final Object msg,
151                          final Throwable t) {
152         return filter(level, currentContextData());
153     }
154 
155     @Override
156     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
157                          final Object... params) {
158         return filter(level, currentContextData());
159     }
160 
161     private ReadOnlyStringMap currentContextData() {
162         return injector.rawContextData();
163     }
164 
165     @Override
166     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
167             final Object p0) {
168         return filter(level, currentContextData());
169     }
170 
171     @Override
172     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
173             final Object p0, final Object p1) {
174         return filter(level, currentContextData());
175     }
176 
177     @Override
178     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
179             final Object p0, final Object p1, final Object p2) {
180         return filter(level, currentContextData());
181     }
182 
183     @Override
184     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
185             final Object p0, final Object p1, final Object p2, final Object p3) {
186         return filter(level, currentContextData());
187     }
188 
189     @Override
190     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
191             final Object p0, final Object p1, final Object p2, final Object p3,
192             final Object p4) {
193         return filter(level, currentContextData());
194     }
195 
196     @Override
197     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
198             final Object p0, final Object p1, final Object p2, final Object p3,
199             final Object p4, final Object p5) {
200         return filter(level, currentContextData());
201     }
202 
203     @Override
204     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
205             final Object p0, final Object p1, final Object p2, final Object p3,
206             final Object p4, final Object p5, final Object p6) {
207         return filter(level, currentContextData());
208     }
209 
210     @Override
211     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
212             final Object p0, final Object p1, final Object p2, final Object p3,
213             final Object p4, final Object p5, final Object p6,
214             final Object p7) {
215         return filter(level, currentContextData());
216     }
217 
218     @Override
219     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
220             final Object p0, final Object p1, final Object p2, final Object p3,
221             final Object p4, final Object p5, final Object p6,
222             final Object p7, final Object p8) {
223         return filter(level, currentContextData());
224     }
225 
226     @Override
227     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
228             final Object p0, final Object p1, final Object p2, final Object p3,
229             final Object p4, final Object p5, final Object p6,
230             final Object p7, final Object p8, final Object p9) {
231         return filter(level, currentContextData());
232     }
233 
234     public String getKey() {
235         return this.key;
236     }
237 
238     public Map<String, Level> getLevelMap() {
239         return levelMap;
240     }
241 
242     @Override
243     public int hashCode() {
244         final int prime = 31;
245         int result = super.hashCodeImpl();
246         result = prime * result + ((defaultThreshold == null) ? 0 : defaultThreshold.hashCode());
247         result = prime * result + ((key == null) ? 0 : key.hashCode());
248         result = prime * result + ((levelMap == null) ? 0 : levelMap.hashCode());
249         return result;
250     }
251 
252     @Override
253     public String toString() {
254         final StringBuilder sb = new StringBuilder();
255         sb.append("key=").append(key);
256         sb.append(", default=").append(defaultThreshold);
257         if (levelMap.size() > 0) {
258             sb.append('{');
259             boolean first = true;
260             for (final Map.Entry<String, Level> entry : levelMap.entrySet()) {
261                 if (!first) {
262                     sb.append(", ");
263                     first = false;
264                 }
265                 sb.append(entry.getKey()).append('=').append(entry.getValue());
266             }
267             sb.append('}');
268         }
269         return sb.toString();
270     }
271 }