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 */
017package org.apache.logging.log4j.core.filter;
018
019import java.util.HashMap;
020import java.util.Map;
021
022import org.apache.logging.log4j.Level;
023import org.apache.logging.log4j.Marker;
024import org.apache.logging.log4j.ThreadContext;
025import org.apache.logging.log4j.core.LogEvent;
026import org.apache.logging.log4j.core.Logger;
027import org.apache.logging.log4j.core.config.plugins.Plugin;
028import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
029import org.apache.logging.log4j.core.config.plugins.PluginElement;
030import org.apache.logging.log4j.core.config.plugins.PluginFactory;
031import org.apache.logging.log4j.core.helpers.KeyValuePair;
032import 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)
038public 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.isAtLeastAsSpecificAs(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 levelName The default Level.
123     * @param match The action to perform if a match occurs.
124     * @param mismatch 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 String levelName,
132            @PluginAttribute("onMatch") final String match,
133            @PluginAttribute("onMismatch") final String mismatch) {
134        final Result onMatch = Result.toResult(match);
135        final Result onMismatch = Result.toResult(mismatch);
136        final Map<String, Level> map = new HashMap<String, Level>();
137        for (final KeyValuePair pair : pairs) {
138            map.put(pair.getKey(), Level.toLevel(pair.getValue()));
139        }
140        final Level level = Level.toLevel(levelName, Level.ERROR);
141        return new DynamicThresholdFilter(key, map, level, onMatch, onMismatch);
142    }
143}