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.HashMap;
020    import java.util.Map;
021    
022    import org.apache.logging.log4j.Level;
023    import org.apache.logging.log4j.Marker;
024    import org.apache.logging.log4j.ThreadContext;
025    import org.apache.logging.log4j.core.Filter;
026    import org.apache.logging.log4j.core.LogEvent;
027    import org.apache.logging.log4j.core.Logger;
028    import org.apache.logging.log4j.core.config.Node;
029    import org.apache.logging.log4j.core.config.plugins.Plugin;
030    import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
031    import org.apache.logging.log4j.core.config.plugins.PluginElement;
032    import org.apache.logging.log4j.core.config.plugins.PluginFactory;
033    import org.apache.logging.log4j.core.util.KeyValuePair;
034    import org.apache.logging.log4j.message.Message;
035    
036    /**
037     * Compare against a log level that is associated with an MDC value.
038     */
039    @Plugin(name = "DynamicThresholdFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE, printObject = true)
040    public final class DynamicThresholdFilter extends AbstractFilter {
041    
042        private static final long serialVersionUID = 1L;
043    
044        /**
045         * Create the DynamicThresholdFilter.
046         * @param key The name of the key to compare.
047         * @param pairs An array of value and Level pairs.
048         * @param defaultThreshold The default Level.
049         * @param onMatch The action to perform if a match occurs.
050         * @param onMismatch The action to perform if no match occurs.
051         * @return The DynamicThresholdFilter.
052         */
053        @PluginFactory
054        public static DynamicThresholdFilter createFilter(
055                @PluginAttribute("key") final String key,
056                @PluginElement("Pairs") final KeyValuePair[] pairs,
057                @PluginAttribute("defaultThreshold") final Level defaultThreshold,
058                @PluginAttribute("onMatch") final Result onMatch,
059                @PluginAttribute("onMismatch") final Result onMismatch) {
060            final Map<String, Level> map = new HashMap<String, Level>();
061            for (final KeyValuePair pair : pairs) {
062                map.put(pair.getKey(), Level.toLevel(pair.getValue()));
063            }
064            final Level level = defaultThreshold == null ? Level.ERROR : defaultThreshold;
065            return new DynamicThresholdFilter(key, map, level, onMatch, onMismatch);
066        }
067        private Level defaultThreshold = Level.ERROR;
068        private final String key;
069    
070        private Map<String, Level> levelMap = new HashMap<String, Level>();
071    
072        private DynamicThresholdFilter(final String key, final Map<String, Level> pairs, final Level defaultLevel,
073                                       final Result onMatch, final Result onMismatch) {
074            super(onMatch, onMismatch);
075            if (key == null) {
076                throw new NullPointerException("key cannot be null");
077            }
078            this.key = key;
079            this.levelMap = pairs;
080            this.defaultThreshold = defaultLevel;
081        }
082    
083        @Override
084        public boolean equals(final Object obj) {
085            if (this == obj) {
086                return true;
087            }
088            if (!super.equals(obj)) {
089                return false;
090            }
091            if (getClass() != obj.getClass()) {
092                return false;
093            }
094            final DynamicThresholdFilter other = (DynamicThresholdFilter) obj;
095            if (defaultThreshold == null) {
096                if (other.defaultThreshold != null) {
097                    return false;
098                }
099            } else if (!defaultThreshold.equals(other.defaultThreshold)) {
100                return false;
101            }
102            if (key == null) {
103                if (other.key != null) {
104                    return false;
105                }
106            } else if (!key.equals(other.key)) {
107                return false;
108            }
109            if (levelMap == null) {
110                if (other.levelMap != null) {
111                    return false;
112                }
113            } else if (!levelMap.equals(other.levelMap)) {
114                return false;
115            }
116            return true;
117        }
118    
119        private Result filter(final Level level) {
120            final Object value = ThreadContext.get(key);
121            if (value != null) {
122                Level ctxLevel = levelMap.get(value);
123                if (ctxLevel == null) {
124                    ctxLevel = defaultThreshold;
125                }
126                return level.isMoreSpecificThan(ctxLevel) ? onMatch : onMismatch;
127            }
128            return Result.NEUTRAL;
129    
130        }
131    
132        @Override
133        public Result filter(final LogEvent event) {
134            return filter(event.getLevel());
135        }
136    
137        @Override
138        public Result filter(final Logger logger, final Level level, final Marker marker, final Message msg,
139                             final Throwable t) {
140            return filter(level);
141        }
142    
143        @Override
144        public Result filter(final Logger logger, final Level level, final Marker marker, final Object msg,
145                             final Throwable t) {
146            return filter(level);
147        }
148    
149        @Override
150        public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
151                             final Object... params) {
152            return filter(level);
153        }
154    
155        public String getKey() {
156            return this.key;
157        }
158    
159        public Map<String, Level> getLevelMap() {
160            return levelMap;
161        }
162    
163        @Override
164        public int hashCode() {
165            final int prime = 31;
166            int result = super.hashCode();
167            result = prime * result + ((defaultThreshold == null) ? 0 : defaultThreshold.hashCode());
168            result = prime * result + ((key == null) ? 0 : key.hashCode());
169            result = prime * result + ((levelMap == null) ? 0 : levelMap.hashCode());
170            return result;
171        }
172    
173        @Override
174        public String toString() {
175            final StringBuilder sb = new StringBuilder();
176            sb.append("key=").append(key);
177            sb.append(", default=").append(defaultThreshold);
178            if (levelMap.size() > 0) {
179                sb.append('{');
180                boolean first = true;
181                for (final Map.Entry<String, Level> entry : levelMap.entrySet()) {
182                    if (!first) {
183                        sb.append(", ");
184                        first = false;
185                    }
186                    sb.append(entry.getKey()).append('=').append(entry.getValue());
187                }
188                sb.append('}');
189            }
190            return sb.toString();
191        }
192    }