001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017package org.apache.logging.log4j.core.filter; 018 019import java.util.ArrayList; 020import java.util.HashMap; 021import java.util.Iterator; 022import java.util.List; 023import java.util.Map; 024 025import org.apache.logging.log4j.Level; 026import org.apache.logging.log4j.Marker; 027import org.apache.logging.log4j.core.ContextDataInjector; 028import org.apache.logging.log4j.core.Filter; 029import org.apache.logging.log4j.core.LogEvent; 030import org.apache.logging.log4j.core.Logger; 031import org.apache.logging.log4j.core.config.Node; 032import org.apache.logging.log4j.core.config.plugins.Plugin; 033import org.apache.logging.log4j.core.config.plugins.PluginAliases; 034import org.apache.logging.log4j.core.config.plugins.PluginAttribute; 035import org.apache.logging.log4j.core.config.plugins.PluginElement; 036import org.apache.logging.log4j.core.config.plugins.PluginFactory; 037import org.apache.logging.log4j.core.impl.ContextDataInjectorFactory; 038import org.apache.logging.log4j.core.util.KeyValuePair; 039import org.apache.logging.log4j.message.Message; 040import org.apache.logging.log4j.util.IndexedReadOnlyStringMap; 041import org.apache.logging.log4j.util.PerformanceSensitive; 042import org.apache.logging.log4j.util.ReadOnlyStringMap; 043 044/** 045 * Filter based on a value in the Thread Context Map (MDC). 046 */ 047@Plugin(name = "ThreadContextMapFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE, printObject = true) 048@PluginAliases("ContextMapFilter") 049@PerformanceSensitive("allocation") 050public class ThreadContextMapFilter extends MapFilter { 051 052 private final ContextDataInjector injector = ContextDataInjectorFactory.createInjector(); 053 private final String key; 054 private final String value; 055 056 private final boolean useMap; 057 058 public ThreadContextMapFilter(final Map<String, List<String>> pairs, final boolean oper, final Result onMatch, 059 final Result onMismatch) { 060 super(pairs, oper, onMatch, onMismatch); 061 if (pairs.size() == 1) { 062 final Iterator<Map.Entry<String, List<String>>> iter = pairs.entrySet().iterator(); 063 final Map.Entry<String, List<String>> entry = iter.next(); 064 if (entry.getValue().size() == 1) { 065 this.key = entry.getKey(); 066 this.value = entry.getValue().get(0); 067 this.useMap = false; 068 } else { 069 this.key = null; 070 this.value = null; 071 this.useMap = true; 072 } 073 } else { 074 this.key = null; 075 this.value = null; 076 this.useMap = true; 077 } 078 } 079 080 @Override 081 public Result filter(final Logger logger, final Level level, final Marker marker, final String msg, 082 final Object... params) { 083 return filter(); 084 } 085 086 @Override 087 public Result filter(final Logger logger, final Level level, final Marker marker, final Object msg, 088 final Throwable t) { 089 return filter(); 090 } 091 092 @Override 093 public Result filter(final Logger logger, final Level level, final Marker marker, final Message msg, 094 final Throwable t) { 095 return filter(); 096 } 097 098 private Result filter() { 099 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}