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.IndexedReadOnlyStringMap; 039import org.apache.logging.log4j.util.IndexedStringMap; 040import org.apache.logging.log4j.util.PerformanceSensitive; 041import org.apache.logging.log4j.util.ReadOnlyStringMap; 042import org.apache.logging.log4j.util.SortedArrayStringMap; 043 044/** 045 * A Filter that operates on a Map. 046 */ 047@Plugin(name = "MapFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE, printObject = true) 048@PerformanceSensitive("allocation") 049public class MapFilter extends AbstractFilter { 050 051 private final IndexedStringMap map; 052 private final boolean isAnd; 053 054 protected MapFilter(final Map<String, List<String>> map, final boolean oper, final Result onMatch, final Result onMismatch) { 055 super(onMatch, onMismatch); 056 this.isAnd = oper; 057 Objects.requireNonNull(map, "map cannot be null"); 058 059 this.map = new SortedArrayStringMap(map.size()); 060 for (final Map.Entry<String, List<String>> entry : map.entrySet()) { 061 this.map.putValue(entry.getKey(), entry.getValue()); 062 } 063 } 064 065 @Override 066 public Result filter(final Logger logger, final Level level, final Marker marker, final Message msg, 067 final Throwable t) { 068 if (msg instanceof MapMessage) { 069 return filter((MapMessage<?, ?>) msg) ? onMatch : onMismatch; 070 } 071 return Result.NEUTRAL; 072 } 073 074 @Override 075 public Result filter(final LogEvent event) { 076 final Message msg = event.getMessage(); 077 if (msg instanceof MapMessage) { 078 return filter((MapMessage<?, ?>) msg) ? onMatch : onMismatch; 079 } 080 return Result.NEUTRAL; 081 } 082 083 protected boolean filter(final MapMessage<?, ?> mapMessage) { 084 boolean match = false; 085 for (int i = 0; i < map.size(); i++) { 086 final String toMatch = mapMessage.get(map.getKeyAt(i)); 087 match = toMatch != null && ((List<String>) map.getValueAt(i)).contains(toMatch); 088 089 if ((!isAnd && match) || (isAnd && !match)) { 090 break; 091 } 092 } 093 return match; 094 } 095 096 protected boolean filter(final Map<String, String> data) { 097 boolean match = false; 098 for (int i = 0; i < map.size(); i++) { 099 final String toMatch = data.get(map.getKeyAt(i)); 100 match = toMatch != null && ((List<String>) map.getValueAt(i)).contains(toMatch); 101 102 if ((!isAnd && match) || (isAnd && !match)) { 103 break; 104 } 105 } 106 return match; 107 } 108 109 protected boolean filter(final ReadOnlyStringMap data) { 110 boolean match = false; 111 for (int i = 0; i < map.size(); i++) { 112 final String toMatch = data.getValue(map.getKeyAt(i)); 113 match = toMatch != null && ((List<String>) map.getValueAt(i)).contains(toMatch); 114 115 if ((!isAnd && match) || (isAnd && !match)) { 116 break; 117 } 118 } 119 return match; 120 } 121 122 @Override 123 public Result filter(final Logger logger, final Level level, final Marker marker, final String msg, 124 final Object p0) { 125 return Result.NEUTRAL; 126 } 127 128 @Override 129 public Result filter(final Logger logger, final Level level, final Marker marker, final String msg, 130 final Object p0, final Object p1) { 131 return Result.NEUTRAL; 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, final Object p2) { 137 return Result.NEUTRAL; 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, final Object p3) { 143 return Result.NEUTRAL; 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 final Object p4) { 150 return Result.NEUTRAL; 151 } 152 153 @Override 154 public Result filter(final Logger logger, final Level level, final Marker marker, final String msg, 155 final Object p0, final Object p1, final Object p2, final Object p3, 156 final Object p4, final Object p5) { 157 return Result.NEUTRAL; 158 } 159 160 @Override 161 public Result filter(final Logger logger, final Level level, final Marker marker, final String msg, 162 final Object p0, final Object p1, final Object p2, final Object p3, 163 final Object p4, final Object p5, final Object p6) { 164 return Result.NEUTRAL; 165 } 166 167 @Override 168 public Result filter(final Logger logger, final Level level, final Marker marker, final String msg, 169 final Object p0, final Object p1, final Object p2, final Object p3, 170 final Object p4, final Object p5, final Object p6, 171 final Object p7) { 172 return Result.NEUTRAL; 173 } 174 175 @Override 176 public Result filter(final Logger logger, final Level level, final Marker marker, final String msg, 177 final Object p0, final Object p1, final Object p2, final Object p3, 178 final Object p4, final Object p5, final Object p6, 179 final Object p7, final Object p8) { 180 return Result.NEUTRAL; 181 } 182 183 @Override 184 public Result filter(final Logger logger, final Level level, final Marker marker, final String msg, 185 final Object p0, final Object p1, final Object p2, final Object p3, 186 final Object p4, final Object p5, final Object p6, 187 final Object p7, final Object p8, final Object p9) { 188 return Result.NEUTRAL; 189 } 190 191 @Override 192 public String toString() { 193 final StringBuilder sb = new StringBuilder(); 194 sb.append("isAnd=").append(isAnd); 195 if (map.size() > 0) { 196 sb.append(", {"); 197 for (int i = 0; i < map.size(); i++) { 198 if (i > 0) { 199 sb.append(", "); 200 } 201 final List<String> list = map.getValueAt(i); 202 final String value = list.size() > 1 ? list.get(0) : list.toString(); 203 sb.append(map.getKeyAt(i)).append('=').append(value); 204 } 205 sb.append('}'); 206 } 207 return sb.toString(); 208 } 209 210 protected boolean isAnd() { 211 return isAnd; 212 } 213 214 /** @deprecated use {@link #getStringMap()} instead */ 215 @Deprecated 216 protected Map<String, List<String>> getMap() { 217 final Map<String, List<String>> result = new HashMap<>(map.size()); 218 map.forEach((key, value) -> result.put(key, (List<String>) value)); 219 return result; 220 } 221 222 /** 223 * Returns the IndexedStringMap with {@code List<String>} values that this MapFilter was constructed with. 224 * @return the IndexedStringMap with {@code List<String>} values to match against 225 * @since 2.8 226 */ 227 protected IndexedReadOnlyStringMap getStringMap() { 228 return map; 229 } 230 231 // TODO Consider refactoring to use AbstractFilter.AbstractFilterBuilder 232 @PluginFactory 233 public static MapFilter createFilter( 234 @PluginElement("Pairs") final KeyValuePair[] pairs, 235 @PluginAttribute("operator") final String oper, 236 @PluginAttribute("onMatch") final Result match, 237 @PluginAttribute("onMismatch") final Result mismatch) { 238 if (pairs == null || pairs.length == 0) { 239 LOGGER.error("keys and values must be specified for the MapFilter"); 240 return null; 241 } 242 final Map<String, List<String>> map = new HashMap<>(); 243 for (final KeyValuePair pair : pairs) { 244 final String key = pair.getKey(); 245 if (key == null) { 246 LOGGER.error("A null key is not valid in MapFilter"); 247 continue; 248 } 249 final String value = pair.getValue(); 250 if (value == null) { 251 LOGGER.error("A null value for key " + key + " is not allowed in MapFilter"); 252 continue; 253 } 254 List<String> list = map.get(pair.getKey()); 255 if (list != null) { 256 list.add(value); 257 } else { 258 list = new ArrayList<>(); 259 list.add(value); 260 map.put(pair.getKey(), list); 261 } 262 } 263 if (map.isEmpty()) { 264 LOGGER.error("MapFilter is not configured with any valid key value pairs"); 265 return null; 266 } 267 final boolean isAnd = oper == null || !oper.equalsIgnoreCase("or"); 268 return new MapFilter(map, isAnd, match, mismatch); 269 } 270}