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