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.LogEvent;
026    import org.apache.logging.log4j.core.Logger;
027    import org.apache.logging.log4j.core.config.plugins.Plugin;
028    import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
029    import org.apache.logging.log4j.core.config.plugins.PluginElement;
030    import org.apache.logging.log4j.core.config.plugins.PluginFactory;
031    import org.apache.logging.log4j.core.util.KeyValuePair;
032    import org.apache.logging.log4j.message.Message;
033    
034    /**
035     * Compare against a log level that is associated with an MDC value.
036     */
037    @Plugin(name = "DynamicThresholdFilter", category = "Core", elementType = "filter", printObject = true)
038    public final class DynamicThresholdFilter extends AbstractFilter {
039        private Map<String, Level> levelMap = new HashMap<String, Level>();
040        private Level defaultThreshold = Level.ERROR;
041        private final String key;
042    
043        private DynamicThresholdFilter(final String key, final Map<String, Level> pairs, final Level defaultLevel,
044                                       final Result onMatch, final Result onMismatch) {
045            super(onMatch, onMismatch);
046            if (key == null) {
047                throw new NullPointerException("key cannot be null");
048            }
049            this.key = key;
050            this.levelMap = pairs;
051            this.defaultThreshold = defaultLevel;
052        }
053    
054        public String getKey() {
055            return this.key;
056        }
057    
058        @Override
059        public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
060                             final Object... params) {
061            return filter(level);
062        }
063    
064        @Override
065        public Result filter(final Logger logger, final Level level, final Marker marker, final Object msg,
066                             final Throwable t) {
067            return filter(level);
068        }
069    
070        @Override
071        public Result filter(final Logger logger, final Level level, final Marker marker, final Message msg,
072                             final Throwable t) {
073            return filter(level);
074        }
075    
076        @Override
077        public Result filter(final LogEvent event) {
078            return filter(event.getLevel());
079        }
080    
081        private Result filter(final Level level) {
082            final Object value = ThreadContext.get(key);
083            if (value != null) {
084                Level ctxLevel = levelMap.get(value);
085                if (ctxLevel == null) {
086                    ctxLevel = defaultThreshold;
087                }
088                return level.isMoreSpecificThan(ctxLevel) ? onMatch : onMismatch;
089            }
090            return Result.NEUTRAL;
091    
092        }
093    
094        public Map<String, Level> getLevelMap() {
095            return levelMap;
096        }
097    
098        @Override
099        public String toString() {
100            final StringBuilder sb = new StringBuilder();
101            sb.append("key=").append(key);
102            sb.append(", default=").append(defaultThreshold);
103            if (levelMap.size() > 0) {
104                sb.append('{');
105                boolean first = true;
106                for (final Map.Entry<String, Level> entry : levelMap.entrySet()) {
107                    if (!first) {
108                        sb.append(", ");
109                        first = false;
110                    }
111                    sb.append(entry.getKey()).append('=').append(entry.getValue());
112                }
113                sb.append('}');
114            }
115            return sb.toString();
116        }
117    
118        /**
119         * Create the DynamicThresholdFilter.
120         * @param key The name of the key to compare.
121         * @param pairs An array of value and Level pairs.
122         * @param defaultThreshold The default Level.
123         * @param onMatch The action to perform if a match occurs.
124         * @param onMismatch The action to perform if no match occurs.
125         * @return The DynamicThresholdFilter.
126         */
127        @PluginFactory
128        public static DynamicThresholdFilter createFilter(
129                @PluginAttribute("key") final String key,
130                @PluginElement("Pairs") final KeyValuePair[] pairs,
131                @PluginAttribute("defaultThreshold") final Level defaultThreshold,
132                @PluginAttribute("onMatch") final Result onMatch,
133                @PluginAttribute("onMismatch") final Result onMismatch) {
134            final Map<String, Level> map = new HashMap<String, Level>();
135            for (final KeyValuePair pair : pairs) {
136                map.put(pair.getKey(), Level.toLevel(pair.getValue()));
137            }
138            final Level level = defaultThreshold == null ? Level.ERROR : defaultThreshold;
139            return new DynamicThresholdFilter(key, map, level, onMatch, onMismatch);
140        }
141    }