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 */
017 package org.apache.logging.log4j.core.filter;
018
019 import java.util.ArrayList;
020 import java.util.HashMap;
021 import java.util.Iterator;
022 import java.util.List;
023 import java.util.Map;
024
025 import org.apache.logging.log4j.Level;
026 import org.apache.logging.log4j.Marker;
027 import org.apache.logging.log4j.ThreadContext;
028 import org.apache.logging.log4j.core.Filter;
029 import org.apache.logging.log4j.core.LogEvent;
030 import org.apache.logging.log4j.core.Logger;
031 import org.apache.logging.log4j.core.config.Node;
032 import org.apache.logging.log4j.core.config.plugins.Plugin;
033 import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
034 import org.apache.logging.log4j.core.config.plugins.PluginElement;
035 import org.apache.logging.log4j.core.config.plugins.PluginFactory;
036 import org.apache.logging.log4j.core.util.KeyValuePair;
037 import org.apache.logging.log4j.message.Message;
038
039 /**
040 * Filter based on a value in the Thread Context Map (MDC).
041 */
042 @Plugin(name = "ThreadContextMapFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE, printObject = true)
043 public class ThreadContextMapFilter extends MapFilter {
044
045 private static final long serialVersionUID = 1L;
046
047 private final String key;
048 private final String value;
049
050 private final boolean useMap;
051
052 public ThreadContextMapFilter(final Map<String, List<String>> pairs, final boolean oper, final Result onMatch,
053 final Result onMismatch) {
054 super(pairs, oper, onMatch, onMismatch);
055 if (pairs.size() == 1) {
056 final Iterator<Map.Entry<String, List<String>>> iter = pairs.entrySet().iterator();
057 final Map.Entry<String, List<String>> entry = iter.next();
058 if (entry.getValue().size() == 1) {
059 this.key = entry.getKey();
060 this.value = entry.getValue().get(0);
061 this.useMap = false;
062 } else {
063 this.key = null;
064 this.value = null;
065 this.useMap = true;
066 }
067 } else {
068 this.key = null;
069 this.value = null;
070 this.useMap = true;
071 }
072 }
073
074 @Override
075 public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
076 final Object... params) {
077 return filter();
078 }
079
080 @Override
081 public Result filter(final Logger logger, final Level level, final Marker marker, final Object msg,
082 final Throwable t) {
083 return filter();
084 }
085
086 @Override
087 public Result filter(final Logger logger, final Level level, final Marker marker, final Message msg,
088 final Throwable t) {
089 return filter();
090 }
091
092 private Result filter() {
093 boolean match = false;
094 if (useMap) {
095 for (final Map.Entry<String, List<String>> entry : getMap().entrySet()) {
096 final String toMatch = ThreadContext.get(entry.getKey());
097 if (toMatch != null) {
098 match = entry.getValue().contains(toMatch);
099 } else {
100 match = false;
101 }
102 if ((!isAnd() && match) || (isAnd() && !match)) {
103 break;
104 }
105 }
106 } else {
107 match = value.equals(ThreadContext.get(key));
108 }
109 return match ? onMatch : onMismatch;
110 }
111
112 @Override
113 public Result filter(final LogEvent event) {
114 return super.filter(event.getContextMap()) ? onMatch : onMismatch;
115 }
116
117 @PluginFactory
118 public static ThreadContextMapFilter createFilter(
119 @PluginElement("Pairs") final KeyValuePair[] pairs,
120 @PluginAttribute("operator") final String oper,
121 @PluginAttribute("onMatch") final Result match,
122 @PluginAttribute("onMismatch") final Result mismatch) {
123 if (pairs == null || pairs.length == 0) {
124 LOGGER.error("key and value pairs must be specified for the ThreadContextMapFilter");
125 return null;
126 }
127 final Map<String, List<String>> map = new HashMap<String, List<String>>();
128 for (final KeyValuePair pair : pairs) {
129 final String key = pair.getKey();
130 if (key == null) {
131 LOGGER.error("A null key is not valid in MapFilter");
132 continue;
133 }
134 final String value = pair.getValue();
135 if (value == null) {
136 LOGGER.error("A null value for key " + key + " is not allowed in MapFilter");
137 continue;
138 }
139 List<String> list = map.get(pair.getKey());
140 if (list != null) {
141 list.add(value);
142 } else {
143 list = new ArrayList<String>();
144 list.add(value);
145 map.put(pair.getKey(), list);
146 }
147 }
148 if (map.isEmpty()) {
149 LOGGER.error("ThreadContextMapFilter is not configured with any valid key value pairs");
150 return null;
151 }
152 final boolean isAnd = oper == null || !oper.equalsIgnoreCase("or");
153 return new ThreadContextMapFilter(map, isAnd, match, mismatch);
154 }
155 }