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.appender.rewrite;
018
019import java.util.HashMap;
020import java.util.Map;
021
022import org.apache.logging.log4j.Logger;
023import org.apache.logging.log4j.core.Core;
024import org.apache.logging.log4j.core.LogEvent;
025import org.apache.logging.log4j.core.config.plugins.Plugin;
026import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
027import org.apache.logging.log4j.core.config.plugins.PluginElement;
028import org.apache.logging.log4j.core.config.plugins.PluginFactory;
029import org.apache.logging.log4j.core.impl.Log4jLogEvent;
030import org.apache.logging.log4j.core.util.KeyValuePair;
031import org.apache.logging.log4j.message.MapMessage;
032import org.apache.logging.log4j.message.Message;
033import org.apache.logging.log4j.status.StatusLogger;
034
035/**
036 * This policy modifies events by replacing or possibly adding keys and values to the MapMessage.
037 */
038@Plugin(name = "MapRewritePolicy", category = Core.CATEGORY_NAME, elementType = "rewritePolicy", printObject = true)
039public final class MapRewritePolicy implements RewritePolicy {
040
041    /**
042     * Allow subclasses access to the status logger without creating another instance.
043     */
044    protected static final Logger LOGGER = StatusLogger.getLogger();
045
046    private final Map<String, Object> map;
047
048    private final Mode mode;
049
050    private MapRewritePolicy(final Map<String, Object> map, final Mode mode) {
051        this.map = map;
052        this.mode = mode;
053    }
054
055    /**
056     * Rewrite the event.
057     * @param source a logging event that may be returned or
058     * used to create a new logging event.
059     * @return The LogEvent after rewriting.
060     */
061    @Override
062    public LogEvent rewrite(final LogEvent source) {
063        final Message msg = source.getMessage();
064        if (msg == null || !(msg instanceof MapMessage)) {
065            return source;
066        }
067
068        @SuppressWarnings("unchecked")
069        final MapMessage<?, Object> mapMsg = (MapMessage<?, Object>) msg;
070        final Map<String, Object> newMap = new HashMap<>(mapMsg.getData());
071        switch (mode) {
072            case Add: {
073                newMap.putAll(map);
074                break;
075            }
076            default: {
077                for (final Map.Entry<String, Object> entry : map.entrySet()) {
078                    if (newMap.containsKey(entry.getKey())) {
079                        newMap.put(entry.getKey(), entry.getValue());
080                    }
081                }
082            }
083        }
084        final Message message = mapMsg.newInstance(newMap);
085        return new Log4jLogEvent.Builder(source).setMessage(message).build();
086    }
087
088    /**
089     * An enumeration to identify whether keys not in the MapMessage should be added or whether only existing
090     * keys should be updated.
091     */
092    public enum Mode {
093
094        /**
095         * Keys should be added.
096         */
097        Add,
098
099        /**
100         * Keys should be updated.
101         */
102        Update
103    }
104
105    @Override
106    public String toString() {
107        final StringBuilder sb = new StringBuilder();
108        sb.append("mode=").append(mode);
109        sb.append(" {");
110        boolean first = true;
111        for (final Map.Entry<String, Object> entry : map.entrySet()) {
112            if (!first) {
113                sb.append(", ");
114            }
115            sb.append(entry.getKey()).append('=').append(entry.getValue());
116            first = false;
117        }
118        sb.append('}');
119        return sb.toString();
120    }
121
122    /**
123     * The factory method to create the MapRewritePolicy.
124     * @param mode The string representation of the Mode.
125     * @param pairs key/value pairs for the new Map keys and values.
126     * @return The MapRewritePolicy.
127     */
128    @PluginFactory
129    public static MapRewritePolicy createPolicy(
130            @PluginAttribute("mode") final String mode,
131            @PluginElement("KeyValuePair") final KeyValuePair[] pairs) {
132        Mode op = mode == null ? op = Mode.Add : Mode.valueOf(mode);
133        if (pairs == null || pairs.length == 0) {
134            LOGGER.error("keys and values must be specified for the MapRewritePolicy");
135            return null;
136        }
137        final Map<String, Object> map = new HashMap<>();
138        for (final KeyValuePair pair : pairs) {
139            final String key = pair.getKey();
140            if (key == null) {
141                LOGGER.error("A null key is not valid in MapRewritePolicy");
142                continue;
143            }
144            final String value = pair.getValue();
145            if (value == null) {
146                LOGGER.error("A null value for key " + key + " is not allowed in MapRewritePolicy");
147                continue;
148            }
149            map.put(pair.getKey(), pair.getValue());
150        }
151        if (map.isEmpty()) {
152            LOGGER.error("MapRewritePolicy is not configured with any valid key value pairs");
153            return null;
154        }
155        return new MapRewritePolicy(map, op);
156    }
157}