    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    }