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     // TODO Consider refactoring to use AbstractFilter.AbstractFilterBuilder
198     @PluginFactory
199     public static ThreadContextMapFilter createFilter(
200             @PluginElement("Pairs") final KeyValuePair[] pairs,
201             @PluginAttribute("operator") final String oper,
202             @PluginAttribute("onMatch") final Result match,
203             @PluginAttribute("onMismatch") final Result mismatch) {
204         if (pairs == null || pairs.length == 0) {
205             LOGGER.error("key and value pairs must be specified for the ThreadContextMapFilter");
206             return null;
207         }
208         final Map<String, List<String>> map = new HashMap<>();
209         for (final KeyValuePair pair : pairs) {
210             final String key = pair.getKey();
211             if (key == null) {
212                 LOGGER.error("A null key is not valid in MapFilter");
213                 continue;
214             }
215             final String value = pair.getValue();
216             if (value == null) {
217                 LOGGER.error("A null value for key " + key + " is not allowed in MapFilter");
218                 continue;
219             }
220             List<String> list = map.get(pair.getKey());
221             if (list != null) {
222                 list.add(value);
223             } else {
224                 list = new ArrayList<>();
225                 list.add(value);
226                 map.put(pair.getKey(), list);
227             }
228         }
229         if (map.isEmpty()) {
230             LOGGER.error("ThreadContextMapFilter is not configured with any valid key value pairs");
231             return null;
232         }
233         final boolean isAnd = oper == null || !oper.equalsIgnoreCase("or");
234         return new ThreadContextMapFilter(map, isAnd, match, mismatch);
235     }
236 }