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      @PluginFactory
60      public static DynamicThresholdFilter createFilter(
61              @PluginAttribute("key") final String key,
62              @PluginElement("Pairs") final KeyValuePair[] pairs,
63              @PluginAttribute("defaultThreshold") final Level defaultThreshold,
64              @PluginAttribute("onMatch") final Result onMatch,
65              @PluginAttribute("onMismatch") final Result onMismatch) {
66          final Map<String, Level> map = new HashMap<>();
67          for (final KeyValuePair pair : pairs) {
68              map.put(pair.getKey(), Level.toLevel(pair.getValue()));
69          }
70          final Level level = defaultThreshold == null ? Level.ERROR : defaultThreshold;
71          return new DynamicThresholdFilter(key, map, level, onMatch, onMismatch);
72      }
73  
74      private Level defaultThreshold = Level.ERROR;
75      private final String key;
76      private final ContextDataInjector injector = ContextDataInjectorFactory.createInjector();
77      private Map<String, Level> levelMap = new HashMap<>();
78  
79      private DynamicThresholdFilter(final String key, final Map<String, Level> pairs, final Level defaultLevel,
80                                     final Result onMatch, final Result onMismatch) {
81          super(onMatch, onMismatch);
82          Objects.requireNonNull(key, "key cannot be null");
83          this.key = key;
84          this.levelMap = pairs;
85          this.defaultThreshold = defaultLevel;
86      }
87  
88      @Override
89      public boolean equals(final Object obj) {
90          if (this == obj) {
91              return true;
92          }
93          if (!super.equalsImpl(obj)) {
94              return false;
95          }
96          if (getClass() != obj.getClass()) {
97              return false;
98          }
99          final DynamicThresholdFilter other = (DynamicThresholdFilter) obj;
100         if (defaultThreshold == null) {
101             if (other.defaultThreshold != null) {
102                 return false;
103             }
104         } else if (!defaultThreshold.equals(other.defaultThreshold)) {
105             return false;
106         }
107         if (key == null) {
108             if (other.key != null) {
109                 return false;
110             }
111         } else if (!key.equals(other.key)) {
112             return false;
113         }
114         if (levelMap == null) {
115             if (other.levelMap != null) {
116                 return false;
117             }
118         } else if (!levelMap.equals(other.levelMap)) {
119             return false;
120         }
121         return true;
122     }
123 
124     private Result filter(final Level level, final ReadOnlyStringMap contextMap) {
125         final String value = contextMap.getValue(key);
126         if (value != null) {
127             Level ctxLevel = levelMap.get(value);
128             if (ctxLevel == null) {
129                 ctxLevel = defaultThreshold;
130             }
131             return level.isMoreSpecificThan(ctxLevel) ? onMatch : onMismatch;
132         }
133         return Result.NEUTRAL;
134 
135     }
136 
137     @Override
138     public Result filter(final LogEvent event) {
139         return filter(event.getLevel(), event.getContextData());
140     }
141 
142     @Override
143     public Result filter(final Logger logger, final Level level, final Marker marker, final Message msg,
144                          final Throwable t) {
145         return filter(level, currentContextData());
146     }
147 
148     @Override
149     public Result filter(final Logger logger, final Level level, final Marker marker, final Object msg,
150                          final Throwable t) {
151         return filter(level, currentContextData());
152     }
153 
154     @Override
155     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
156                          final Object... params) {
157         return filter(level, currentContextData());
158     }
159 
160     private ReadOnlyStringMap currentContextData() {
161         return injector.rawContextData();
162     }
163 
164     @Override
165     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
166             final Object p0) {
167         return filter(level, currentContextData());
168     }
169 
170     @Override
171     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
172             final Object p0, final Object p1) {
173         return filter(level, currentContextData());
174     }
175 
176     @Override
177     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
178             final Object p0, final Object p1, final Object p2) {
179         return filter(level, currentContextData());
180     }
181 
182     @Override
183     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
184             final Object p0, final Object p1, final Object p2, final Object p3) {
185         return filter(level, currentContextData());
186     }
187 
188     @Override
189     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
190             final Object p0, final Object p1, final Object p2, final Object p3,
191             final Object p4) {
192         return filter(level, currentContextData());
193     }
194 
195     @Override
196     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
197             final Object p0, final Object p1, final Object p2, final Object p3,
198             final Object p4, final Object p5) {
199         return filter(level, currentContextData());
200     }
201 
202     @Override
203     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
204             final Object p0, final Object p1, final Object p2, final Object p3,
205             final Object p4, final Object p5, final Object p6) {
206         return filter(level, currentContextData());
207     }
208 
209     @Override
210     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
211             final Object p0, final Object p1, final Object p2, final Object p3,
212             final Object p4, final Object p5, final Object p6,
213             final Object p7) {
214         return filter(level, currentContextData());
215     }
216 
217     @Override
218     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
219             final Object p0, final Object p1, final Object p2, final Object p3,
220             final Object p4, final Object p5, final Object p6,
221             final Object p7, final Object p8) {
222         return filter(level, currentContextData());
223     }
224 
225     @Override
226     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
227             final Object p0, final Object p1, final Object p2, final Object p3,
228             final Object p4, final Object p5, final Object p6,
229             final Object p7, final Object p8, final Object p9) {
230         return filter(level, currentContextData());
231     }
232 
233     public String getKey() {
234         return this.key;
235     }
236 
237     public Map<String, Level> getLevelMap() {
238         return levelMap;
239     }
240 
241     @Override
242     public int hashCode() {
243         final int prime = 31;
244         int result = super.hashCodeImpl();
245         result = prime * result + ((defaultThreshold == null) ? 0 : defaultThreshold.hashCode());
246         result = prime * result + ((key == null) ? 0 : key.hashCode());
247         result = prime * result + ((levelMap == null) ? 0 : levelMap.hashCode());
248         return result;
249     }
250 
251     @Override
252     public String toString() {
253         final StringBuilder sb = new StringBuilder();
254         sb.append("key=").append(key);
255         sb.append(", default=").append(defaultThreshold);
256         if (levelMap.size() > 0) {
257             sb.append('{');
258             boolean first = true;
259             for (final Map.Entry<String, Level> entry : levelMap.entrySet()) {
260                 if (!first) {
261                     sb.append(", ");
262                     first = false;
263                 }
264                 sb.append(entry.getKey()).append('=').append(entry.getValue());
265             }
266             sb.append('}');
267         }
268         return sb.toString();
269     }
270 }