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.List; 022import java.util.Map; 023import java.util.Objects; 024 025import org.apache.logging.log4j.Level; 026import org.apache.logging.log4j.Marker; 027import org.apache.logging.log4j.core.Filter; 028import org.apache.logging.log4j.core.LogEvent; 029import org.apache.logging.log4j.core.Logger; 030import org.apache.logging.log4j.core.config.Node; 031import org.apache.logging.log4j.core.config.plugins.Plugin; 032import org.apache.logging.log4j.core.config.plugins.PluginAttribute; 033import org.apache.logging.log4j.core.config.plugins.PluginElement; 034import org.apache.logging.log4j.core.config.plugins.PluginFactory; 035import org.apache.logging.log4j.core.util.KeyValuePair; 036import org.apache.logging.log4j.message.MapMessage; 037import org.apache.logging.log4j.message.Message; 038import org.apache.logging.log4j.util.BiConsumer; 039import org.apache.logging.log4j.util.IndexedReadOnlyStringMap; 040import org.apache.logging.log4j.util.IndexedStringMap; 041import org.apache.logging.log4j.util.PerformanceSensitive; 042import org.apache.logging.log4j.util.ReadOnlyStringMap; 043import org.apache.logging.log4j.util.SortedArrayStringMap; 044 045/** 046 * A Filter that operates on a Map. 047 */ 048@Plugin(name = "MapFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE, printObject = true) 049@PerformanceSensitive("allocation") 050public class MapFilter extends AbstractFilter { 051 052 private final IndexedStringMap map; 053 private final boolean isAnd; 054 055 protected MapFilter(final Map<String, List<String>> map, final boolean oper, final Result onMatch, final Result onMismatch) { 056 super(onMatch, onMismatch); 057 this.isAnd = oper; 058 Objects.requireNonNull(map, "map cannot be null"); 059 060 this.map = new SortedArrayStringMap(map.size()); 061 for (final Map.Entry<String, List<String>> entry : map.entrySet()) { 062 this.map.putValue(entry.getKey(), entry.getValue()); 063 } 064 } 065 066 @Override 067 public Result filter(final Logger logger, final Level level, final Marker marker, final Message msg, 068 final Throwable t) { 069 if (msg instanceof MapMessage) { 070 return filter((MapMessage<?, ?>) msg) ? onMatch : onMismatch; 071 } 072 return Result.NEUTRAL; 073 } 074 075 @Override 076 public Result filter(final LogEvent event) { 077 final Message msg = event.getMessage(); 078 if (msg instanceof MapMessage) { 079 return filter((MapMessage<?, ?>) msg) ? onMatch : onMismatch; 080 } 081 return Result.NEUTRAL; 082 } 083 084 protected boolean filter(final MapMessage<?, ?> mapMessage) { 085 boolean match = false; 086 for (int i = 0; i < map.size(); i++) { 087 final String toMatch = mapMessage.get(map.getKeyAt(i)); 088 match = toMatch != null && ((List<String>) map.getValueAt(i)).contains(toMatch); 089 090 if ((!isAnd && match) || (isAnd && !match)) { 091 break; 092 } 093 } 094 return match; 095 } 096 097 protected boolean filter(final Map<String, String> data) { 098 boolean match = false; 099 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}