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 }