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