Index: log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/MapPatternConverterTest.java =================================================================== --- log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/MapPatternConverterTest.java (revision 1530365) +++ log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/MapPatternConverterTest.java (working copy) @@ -38,10 +38,7 @@ msg.put("verb", "love"); msg.put("object", "Log4j"); final MapPatternConverter converter = MapPatternConverter.newInstance(null); - final LogEvent event = new Log4jLogEvent("MyLogger", null, null, Level.DEBUG, msg, null); - final StringBuilder sb = new StringBuilder(); - converter.format(event, sb); - final String str = sb.toString(); + final String str = formatEvent(msg, converter); String expected = "subject=I"; assertTrue("Missing or incorrect subject. Expected " + expected + ", actual " + str, str.contains(expected)); expected = "verb=love"; @@ -58,11 +55,27 @@ msg.put("verb", "love"); msg.put("object", "Log4j"); final MapPatternConverter converter = MapPatternConverter.newInstance(new String[] {"object"}); + final String str = formatEvent(msg, converter); + final String expected = "Log4j"; + assertEquals(expected, str); + } + + @Test + public void testConverterWithKeyAndDefault() { + + final MapMessage msg = new MapMessage(); + msg.put("subject", "I"); + msg.put("verb", "love"); + final MapPatternConverter converter = MapPatternConverter.newInstance(new String[] {"object:-Log4j"}); + final String str = formatEvent(msg, converter); + final String expected = "Log4j"; + assertEquals(expected, str); + } + + private String formatEvent(MapMessage msg, MapPatternConverter converter) { final LogEvent event = new Log4jLogEvent("MyLogger", null, null, Level.DEBUG, msg, null); final StringBuilder sb = new StringBuilder(); converter.format(event, sb); - final String str = sb.toString(); - final String expected = "Log4j"; - assertEquals(expected, str); + return sb.toString(); } } Index: log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MapPatternConverter.java =================================================================== --- log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MapPatternConverter.java (revision 1530365) +++ log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MapPatternConverter.java (working copy) @@ -17,8 +17,6 @@ package org.apache.logging.log4j.core.pattern; import java.util.Map; -import java.util.Set; -import java.util.TreeSet; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.config.plugins.Plugin; @@ -32,11 +30,7 @@ */ @Plugin(name = "MapPatternConverter", category = "Converter") @ConverterKeys({"K", "map", "MAP" }) -public final class MapPatternConverter extends LogEventPatternConverter { - /** - * Name of property to output. - */ - private final String key; +public final class MapPatternConverter extends AbstractMapConverter { /** * Private constructor. @@ -44,8 +38,7 @@ * @param options options, may be null. */ private MapPatternConverter(final String[] options) { - super(options != null && options.length > 0 ? "MAP{" + options[0] + "}" : "MAP", "map"); - key = options != null && options.length > 0 ? options[0] : null; + super("MAP", "map", options != null && options.length > 0 ? options[0] : null); } /** @@ -62,39 +55,10 @@ * {@inheritDoc} */ @Override - public void format(final LogEvent event, final StringBuilder toAppendTo) { - MapMessage msg; + protected Map getMap(LogEvent event) { if (event.getMessage() instanceof MapMessage) { - msg = (MapMessage) event.getMessage(); - } else { - return; + return ((MapMessage) event.getMessage()).getData(); } - final Map map = msg.getData(); - // if there is no additional options, we output every single - // Key/Value pair for the Map in a similar format to Hashtable.toString() - if (key == null) { - if (map.size() == 0) { - toAppendTo.append("{}"); - return; - } - final StringBuilder sb = new StringBuilder("{"); - final Set keys = new TreeSet(map.keySet()); - for (final String key : keys) { - if (sb.length() > 1) { - sb.append(", "); - } - sb.append(key).append("=").append(map.get(key)); - - } - sb.append("}"); - toAppendTo.append(sb); - } else { - // otherwise they just want a single key output - final String val = map.get(key); - - if (val != null) { - toAppendTo.append(val); - } - } + return null; } } Index: log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/AbstractMapConverter.java =================================================================== --- log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/AbstractMapConverter.java (revision 0) +++ log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/AbstractMapConverter.java (revision 0) @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.log4j.core.pattern; + +import org.apache.logging.log4j.core.LogEvent; + +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +/** + * Base class for a converter that appends a map or a specific value from a map. + */ +public abstract class AbstractMapConverter extends LogEventPatternConverter { + + /** Separator between a key and the default value */ + private static final String DEFAULT_SEPARATOR = ":-"; + + /** + * Name of property to output. + */ + private final String key; + + /** + * Value to output if {@link #key} is defined but not contained in the map + */ + private final String defaultValue; + + /** + * Constructs an instance of AbstractMapConverter. + * + * @param name name of converter. + * @param style CSS style for output. + * @param keySpec specific key to retrieve from map or null to retrieve all keys + */ + protected AbstractMapConverter(String name, String style, String keySpec) { + super(keySpec == null ? name : (name + "{" + keySpec + "}"), style); + if (keySpec != null && keySpec.contains(DEFAULT_SEPARATOR)) { + final int defaultPos = keySpec.indexOf(DEFAULT_SEPARATOR); + this.key = keySpec.substring(0, defaultPos); + this.defaultValue = keySpec.substring(defaultPos + DEFAULT_SEPARATOR.length()); + } + else { + this.key = keySpec; + this.defaultValue = null; + } + } + + /** + * {@inheritDoc} + */ + @Override + public void format(final LogEvent event, final StringBuilder toAppendTo) { + final Map map = getMap(event); + if (map == null) { + return; + } + + // if there is no additional options, we output every single + // Key/Value pair for the Map in a similar format to Hashtable.toString() + if (key == null) { + if (map.size() == 0) { + toAppendTo.append("{}"); + return; + } + final StringBuilder sb = new StringBuilder("{"); + final Set keys = new TreeSet(map.keySet()); + for (final String key : keys) { + if (sb.length() > 1) { + sb.append(", "); + } + sb.append(key).append("=").append(map.get(key)); + + } + sb.append("}"); + toAppendTo.append(sb); + } else { + // otherwise they just want a single key output + final String val = getValueOrDefault(map); + + if (val != null) { + toAppendTo.append(val); + } + } + + } + + private String getValueOrDefault(Map map) { + final String value = map.get(key); + return (value != null) ? value : defaultValue; + } + + /** + * + * @param event to be formatted + * @return map used to render output; may be null, in which case no output will be rendered + */ + protected abstract Map getMap(LogEvent event); +} Index: log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MDCPatternConverter.java =================================================================== --- log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MDCPatternConverter.java (revision 1530365) +++ log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MDCPatternConverter.java (working copy) @@ -17,8 +17,6 @@ package org.apache.logging.log4j.core.pattern; import java.util.Map; -import java.util.Set; -import java.util.TreeSet; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.config.plugins.Plugin; @@ -32,20 +30,14 @@ */ @Plugin(name = "MDCPatternConverter", category = "Converter") @ConverterKeys({"X", "mdc", "MDC" }) -public final class MDCPatternConverter extends LogEventPatternConverter { +public final class MDCPatternConverter extends AbstractMapConverter { /** - * Name of property to output. - */ - private final String key; - - /** * Private constructor. * * @param options options, may be null. */ private MDCPatternConverter(final String[] options) { - super(options != null && options.length > 0 ? "MDC{" + options[0] + "}" : "MDC", "mdc"); - key = options != null && options.length > 0 ? options[0] : null; + super("MDC", "mdc", options != null && options.length > 0 ? options[0] : null); } /** @@ -62,35 +54,7 @@ * {@inheritDoc} */ @Override - public void format(final LogEvent event, final StringBuilder toAppendTo) { - final Map contextMap = event.getContextMap(); - // if there is no additional options, we output every single - // Key/Value pair for the MDC in a similar format to Hashtable.toString() - if (key == null) { - - - if (contextMap == null || contextMap.size() == 0) { - toAppendTo.append("{}"); - return; - } - final StringBuilder sb = new StringBuilder("{"); - final Set keys = new TreeSet(contextMap.keySet()); - for (final String key : keys) { - if (sb.length() > 1) { - sb.append(", "); - } - sb.append(key).append("=").append(contextMap.get(key)); - - } - sb.append("}"); - toAppendTo.append(sb); - } else if (contextMap != null) { - // otherwise they just want a single key output - final Object val = contextMap.get(key); - - if (val != null) { - toAppendTo.append(val); - } - } + protected Map getMap(LogEvent event) { + return event.getContextMap(); } }