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