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