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     * Allow subclasses access to the status logger without creating another instance.
042     */
043    protected static final Logger LOGGER = StatusLogger.getLogger();
044
045    private final Map<String, Object> map;
046
047    private final Mode mode;
048
049    private MapRewritePolicy(final Map<String, Object> map, final Mode mode) {
050        this.map = map;
051        this.mode = mode;
052    }
053
054    /**
055     * Rewrite the event.
056     * @param source a logging event that may be returned or
057     * used to create a new logging event.
058     * @return The LogEvent after rewriting.
059     */
060    @Override
061    public LogEvent rewrite(final LogEvent source) {
062        final Message msg = source.getMessage();
063        if (msg == null || !(msg instanceof MapMessage)) {
064            return source;
065        }
066
067        @SuppressWarnings("unchecked")
068        final MapMessage<?, Object> mapMsg = (MapMessage<?, Object>) msg;
069        final Map<String, Object> newMap = new HashMap<>(mapMsg.getData());
070        switch (mode) {
071            case Add: {
072                newMap.putAll(map);
073                break;
074            }
075            default: {
076                for (final Map.Entry<String, Object> entry : map.entrySet()) {
077                    if (newMap.containsKey(entry.getKey())) {
078                        newMap.put(entry.getKey(), entry.getValue());
079                    }
080                }
081            }
082        }
083        final Message message = mapMsg.newInstance(newMap);
084        final LogEvent result = new Log4jLogEvent.Builder(source).setMessage(message).build();
085        return result;
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         * Keys should be added.
095         */
096        Add,
097        /**
098         * Keys should be updated.
099         */
100        Update
101    }
102
103    @Override
104    public String toString() {
105        final StringBuilder sb = new StringBuilder();
106        sb.append("mode=").append(mode);
107        sb.append(" {");
108        boolean first = true;
109        for (final Map.Entry<String, Object> entry : map.entrySet()) {
110            if (!first) {
111                sb.append(", ");
112            }
113            sb.append(entry.getKey()).append('=').append(entry.getValue());
114            first = false;
115        }
116        sb.append('}');
117        return sb.toString();
118    }
119
120    /**
121     * The factory method to create the MapRewritePolicy.
122     * @param mode The string representation of the Mode.
123     * @param pairs key/value pairs for the new Map keys and values.
124     * @return The MapRewritePolicy.
125     */
126    @PluginFactory
127    public static MapRewritePolicy createPolicy(
128            @PluginAttribute("mode") final String mode,
129            @PluginElement("KeyValuePair") final KeyValuePair[] pairs) {
130        Mode op = mode == null ? op = Mode.Add : Mode.valueOf(mode);
131        if (pairs == null || pairs.length == 0) {
132            LOGGER.error("keys and values must be specified for the MapRewritePolicy");
133            return null;
134        }
135        final Map<String, Object> map = new HashMap<>();
136        for (final KeyValuePair pair : pairs) {
137            final String key = pair.getKey();
138            if (key == null) {
139                LOGGER.error("A null key is not valid in MapRewritePolicy");
140                continue;
141            }
142            final String value = pair.getValue();
143            if (value == null) {
144                LOGGER.error("A null value for key " + key + " is not allowed in MapRewritePolicy");
145                continue;
146            }
147            map.put(pair.getKey(), pair.getValue());
148        }
149        if (map.isEmpty()) {
150            LOGGER.error("MapRewritePolicy is not configured with any valid key value pairs");
151            return null;
152        }
153        return new MapRewritePolicy(map, op);
154    }
155}