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.Iterator;
22  import java.util.List;
23  import java.util.Map;
24  
25  import org.apache.logging.log4j.Level;
26  import org.apache.logging.log4j.Marker;
27  import org.apache.logging.log4j.core.ContextDataInjector;
28  import org.apache.logging.log4j.core.Filter;
29  import org.apache.logging.log4j.core.LogEvent;
30  import org.apache.logging.log4j.core.Logger;
31  import org.apache.logging.log4j.core.config.Node;
32  import org.apache.logging.log4j.core.config.plugins.Plugin;
33  import org.apache.logging.log4j.core.config.plugins.PluginAliases;
34  import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
35  import org.apache.logging.log4j.core.config.plugins.PluginElement;
36  import org.apache.logging.log4j.core.config.plugins.PluginFactory;
37  import org.apache.logging.log4j.core.impl.ContextDataInjectorFactory;
38  import org.apache.logging.log4j.core.util.KeyValuePair;
39  import org.apache.logging.log4j.message.Message;
40  import org.apache.logging.log4j.util.IndexedReadOnlyStringMap;
41  import org.apache.logging.log4j.util.PerformanceSensitive;
42  import org.apache.logging.log4j.util.ReadOnlyStringMap;
43  
44  /**
45   * Filter based on a value in the Thread Context Map (MDC).
46   */
47  @Plugin(name = "ThreadContextMapFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE, printObject = true)
48  @PluginAliases("ContextMapFilter")
49  @PerformanceSensitive("allocation")
50  public class ThreadContextMapFilter extends MapFilter {
51  
52      private final ContextDataInjector injector = ContextDataInjectorFactory.createInjector();
53      private final String key;
54      private final String value;
55  
56      private final boolean useMap;
57  
58      public ThreadContextMapFilter(final Map<String, List<String>> pairs, final boolean oper, final Result onMatch,
59                                    final Result onMismatch) {
60          super(pairs, oper, onMatch, onMismatch);
61          if (pairs.size() == 1) {
62              final Iterator<Map.Entry<String, List<String>>> iter = pairs.entrySet().iterator();
63              final Map.Entry<String, List<String>> entry = iter.next();
64              if (entry.getValue().size() == 1) {
65                  this.key = entry.getKey();
66                  this.value = entry.getValue().get(0);
67                  this.useMap = false;
68              } else {
69                  this.key = null;
70                  this.value = null;
71                  this.useMap = true;
72              }
73          } else {
74              this.key = null;
75              this.value = null;
76              this.useMap = true;
77          }
78      }
79  
80      @Override
81      public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
82                           final Object... params) {
83          return filter();
84      }
85  
86      @Override
87      public Result filter(final Logger logger, final Level level, final Marker marker, final Object msg,
88                           final Throwable t) {
89          return filter();
90      }
91  
92      @Override
93      public Result filter(final Logger logger, final Level level, final Marker marker, final Message msg,
94                           final Throwable t) {
95          return filter();
96      }
97  
98      private Result filter() {
99          boolean match = false;
100         if (useMap) {
101             ReadOnlyStringMap currentContextData = null;
102             final IndexedReadOnlyStringMap map = getStringMap();
103             for (int i = 0; i < map.size(); i++) {
104                 if (currentContextData == null) {
105                     currentContextData = currentContextData();
106                 }
107                 final String toMatch = currentContextData.getValue(map.getKeyAt(i));
108                 match = toMatch != null && ((List<String>) map.getValueAt(i)).contains(toMatch);
109                 if ((!isAnd() && match) || (isAnd() && !match)) {
110                     break;
111                 }
112             }
113         } else {
114             match = value.equals(currentContextData().getValue(key));
115         }
116         return match ? onMatch : onMismatch;
117     }
118 
119     private ReadOnlyStringMap currentContextData() {
120         return injector.rawContextData();
121     }
122 
123     @Override
124     public Result filter(final LogEvent event) {
125         return super.filter(event.getContextData()) ? onMatch : onMismatch;
126     }
127 
128     @Override
129     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
130             final Object p0) {
131         return filter();
132     }
133 
134     @Override
135     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
136             final Object p0, final Object p1) {
137         return filter();
138     }
139 
140     @Override
141     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
142             final Object p0, final Object p1, final Object p2) {
143         return filter();
144     }
145 
146     @Override
147     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
148             final Object p0, final Object p1, final Object p2, final Object p3) {
149         return filter();
150     }
151 
152     @Override
153     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
154             final Object p0, final Object p1, final Object p2, final Object p3,
155             final Object p4) {
156         return filter();
157     }
158 
159     @Override
160     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
161             final Object p0, final Object p1, final Object p2, final Object p3,
162             final Object p4, final Object p5) {
163         return filter();
164     }
165 
166     @Override
167     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
168             final Object p0, final Object p1, final Object p2, final Object p3,
169             final Object p4, final Object p5, final Object p6) {
170         return filter();
171     }
172 
173     @Override
174     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
175             final Object p0, final Object p1, final Object p2, final Object p3,
176             final Object p4, final Object p5, final Object p6,
177             final Object p7) {
178         return filter();
179     }
180 
181     @Override
182     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
183             final Object p0, final Object p1, final Object p2, final Object p3,
184             final Object p4, final Object p5, final Object p6,
185             final Object p7, final Object p8) {
186         return filter();
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, final Object p5, final Object p6,
193             final Object p7, final Object p8, final Object p9) {
194         return filter();
195     }
196 
197     @PluginFactory
198     public static ThreadContextMapFilter createFilter(
199             @PluginElement("Pairs") final KeyValuePair[] pairs,
200             @PluginAttribute("operator") final String oper,
201             @PluginAttribute("onMatch") final Result match,
202             @PluginAttribute("onMismatch") final Result mismatch) {
203         if (pairs == null || pairs.length == 0) {
204             LOGGER.error("key and value pairs must be specified for the ThreadContextMapFilter");
205             return null;
206         }
207         final Map<String, List<String>> map = new HashMap<>();
208         for (final KeyValuePair pair : pairs) {
209             final String key = pair.getKey();
210             if (key == null) {
211                 LOGGER.error("A null key is not valid in MapFilter");
212                 continue;
213             }
214             final String value = pair.getValue();
215             if (value == null) {
216                 LOGGER.error("A null value for key " + key + " is not allowed in MapFilter");
217                 continue;
218             }
219             List<String> list = map.get(pair.getKey());
220             if (list != null) {
221                 list.add(value);
222             } else {
223                 list = new ArrayList<>();
224                 list.add(value);
225                 map.put(pair.getKey(), list);
226             }
227         }
228         if (map.isEmpty()) {
229             LOGGER.error("ThreadContextMapFilter is not configured with any valid key value pairs");
230             return null;
231         }
232         final boolean isAnd = oper == null || !oper.equalsIgnoreCase("or");
233         return new ThreadContextMapFilter(map, isAnd, match, mismatch);
234     }
235 }