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.ArrayList;
20  import java.util.HashMap;
21  import java.util.List;
22  import java.util.Map;
23  import java.util.Objects;
24  
25  import org.apache.logging.log4j.Level;
26  import org.apache.logging.log4j.Marker;
27  import org.apache.logging.log4j.core.Filter;
28  import org.apache.logging.log4j.core.LogEvent;
29  import org.apache.logging.log4j.core.Logger;
30  import org.apache.logging.log4j.core.config.Node;
31  import org.apache.logging.log4j.core.config.plugins.Plugin;
32  import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
33  import org.apache.logging.log4j.core.config.plugins.PluginElement;
34  import org.apache.logging.log4j.core.config.plugins.PluginFactory;
35  import org.apache.logging.log4j.core.util.KeyValuePair;
36  import org.apache.logging.log4j.message.MapMessage;
37  import org.apache.logging.log4j.message.Message;
38  import org.apache.logging.log4j.util.BiConsumer;
39  import org.apache.logging.log4j.util.IndexedReadOnlyStringMap;
40  import org.apache.logging.log4j.util.IndexedStringMap;
41  import org.apache.logging.log4j.util.PerformanceSensitive;
42  import org.apache.logging.log4j.util.ReadOnlyStringMap;
43  import org.apache.logging.log4j.util.SortedArrayStringMap;
44  
45  /**
46   * A Filter that operates on a Map.
47   */
48  @Plugin(name = "MapFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE, printObject = true)
49  @PerformanceSensitive("allocation")
50  public class MapFilter extends AbstractFilter {
51  
52      private final IndexedStringMap map;
53      private final boolean isAnd;
54  
55      protected MapFilter(final Map<String, List<String>> map, final boolean oper, final Result onMatch, final Result onMismatch) {
56          super(onMatch, onMismatch);
57          this.isAnd = oper;
58          Objects.requireNonNull(map, "map cannot be null");
59  
60          this.map = new SortedArrayStringMap(map.size());
61          for (final Map.Entry<String, List<String>> entry : map.entrySet()) {
62              this.map.putValue(entry.getKey(), entry.getValue());
63          }
64      }
65  
66      @Override
67      public Result filter(final Logger logger, final Level level, final Marker marker, final Message msg,
68                           final Throwable t) {
69          if (msg instanceof MapMessage) {
70              return filter((MapMessage<?, ?>) msg) ? onMatch : onMismatch;
71          }
72          return Result.NEUTRAL;
73      }
74  
75      @Override
76      public Result filter(final LogEvent event) {
77          final Message msg = event.getMessage();
78          if (msg instanceof MapMessage) {
79              return filter((MapMessage<?, ?>) msg) ? onMatch : onMismatch;
80          }
81          return Result.NEUTRAL;
82      }
83  
84      protected boolean filter(final MapMessage<?, ?> mapMessage) {
85          boolean match = false;
86          for (int i = 0; i < map.size(); i++) {
87              final String toMatch = mapMessage.get(map.getKeyAt(i));
88              match = toMatch != null && ((List<String>) map.getValueAt(i)).contains(toMatch);
89  
90              if ((!isAnd && match) || (isAnd && !match)) {
91                  break;
92              }
93          }
94          return match;
95      }
96  
97      protected boolean filter(final Map<String, String> data) {
98          boolean match = false;
99          for (int i = 0; i < map.size(); i++) {
100             final String toMatch = data.get(map.getKeyAt(i));
101             match = toMatch != null && ((List<String>) map.getValueAt(i)).contains(toMatch);
102 
103             if ((!isAnd && match) || (isAnd && !match)) {
104                 break;
105             }
106         }
107         return match;
108     }
109 
110     protected boolean filter(final ReadOnlyStringMap data) {
111         boolean match = false;
112         for (int i = 0; i < map.size(); i++) {
113             final String toMatch = data.getValue(map.getKeyAt(i));
114             match = toMatch != null && ((List<String>) map.getValueAt(i)).contains(toMatch);
115 
116             if ((!isAnd && match) || (isAnd && !match)) {
117                 break;
118             }
119         }
120         return match;
121     }
122 
123     @Override
124     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
125             final Object p0) {
126         return Result.NEUTRAL;
127     }
128 
129     @Override
130     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
131             final Object p0, final Object p1) {
132         return Result.NEUTRAL;
133     }
134 
135     @Override
136     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
137             final Object p0, final Object p1, final Object p2) {
138         return Result.NEUTRAL;
139     }
140 
141     @Override
142     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
143             final Object p0, final Object p1, final Object p2, final Object p3) {
144         return Result.NEUTRAL;
145     }
146 
147     @Override
148     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
149             final Object p0, final Object p1, final Object p2, final Object p3,
150             final Object p4) {
151         return Result.NEUTRAL;
152     }
153 
154     @Override
155     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
156             final Object p0, final Object p1, final Object p2, final Object p3,
157             final Object p4, final Object p5) {
158         return Result.NEUTRAL;
159     }
160 
161     @Override
162     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
163             final Object p0, final Object p1, final Object p2, final Object p3,
164             final Object p4, final Object p5, final Object p6) {
165         return Result.NEUTRAL;
166     }
167 
168     @Override
169     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
170             final Object p0, final Object p1, final Object p2, final Object p3,
171             final Object p4, final Object p5, final Object p6,
172             final Object p7) {
173         return Result.NEUTRAL;
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, final Object p3,
179             final Object p4, final Object p5, final Object p6,
180             final Object p7, final Object p8) {
181         return Result.NEUTRAL;
182     }
183 
184     @Override
185     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
186             final Object p0, final Object p1, final Object p2, final Object p3,
187             final Object p4, final Object p5, final Object p6,
188             final Object p7, final Object p8, final Object p9) {
189         return Result.NEUTRAL;
190     }
191 
192     @Override
193     public String toString() {
194         final StringBuilder sb = new StringBuilder();
195         sb.append("isAnd=").append(isAnd);
196         if (map.size() > 0) {
197             sb.append(", {");
198             for (int i = 0; i < map.size(); i++) {
199                 if (i > 0) {
200                     sb.append(", ");
201                 }
202                 final List<String> list = map.getValueAt(i);
203                 final String value = list.size() > 1 ? list.get(0) : list.toString();
204                 sb.append(map.getKeyAt(i)).append('=').append(value);
205             }
206             sb.append('}');
207         }
208         return sb.toString();
209     }
210 
211     protected boolean isAnd() {
212         return isAnd;
213     }
214 
215     /** @deprecated  use {@link #getStringMap()} instead */
216     @Deprecated
217     protected Map<String, List<String>> getMap() {
218         final Map<String, List<String>> result = new HashMap<>(map.size());
219         map.forEach(new BiConsumer<String, List<String>>() {
220             @Override
221             public void accept(final String key, final List<String> value) {
222                 result.put(key, value);
223             }
224         });
225         return result;
226     }
227 
228     /**
229      * Returns the IndexedStringMap with {@code List<String>} values that this MapFilter was constructed with.
230      * @return the IndexedStringMap with {@code List<String>} values to match against
231      * @since 2.8
232      */
233     protected IndexedReadOnlyStringMap getStringMap() {
234         return map;
235     }
236 
237     // TODO Consider refactoring to use AbstractFilter.AbstractFilterBuilder
238     @PluginFactory
239     public static MapFilter createFilter(
240             @PluginElement("Pairs") final KeyValuePair[] pairs,
241             @PluginAttribute("operator") final String oper,
242             @PluginAttribute("onMatch") final Result match,
243             @PluginAttribute("onMismatch") final Result mismatch) {
244         if (pairs == null || pairs.length == 0) {
245             LOGGER.error("keys and values must be specified for the MapFilter");
246             return null;
247         }
248         final Map<String, List<String>> map = new HashMap<>();
249         for (final KeyValuePair pair : pairs) {
250             final String key = pair.getKey();
251             if (key == null) {
252                 LOGGER.error("A null key is not valid in MapFilter");
253                 continue;
254             }
255             final String value = pair.getValue();
256             if (value == null) {
257                 LOGGER.error("A null value for key " + key + " is not allowed in MapFilter");
258                 continue;
259             }
260             List<String> list = map.get(pair.getKey());
261             if (list != null) {
262                 list.add(value);
263             } else {
264                 list = new ArrayList<>();
265                 list.add(value);
266                 map.put(pair.getKey(), list);
267             }
268         }
269         if (map.isEmpty()) {
270             LOGGER.error("MapFilter is not configured with any valid key value pairs");
271             return null;
272         }
273         final boolean isAnd = oper == null || !oper.equalsIgnoreCase("or");
274         return new MapFilter(map, isAnd, match, mismatch);
275     }
276 }