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    }