logging-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rgo...@apache.org
Subject svn commit: r1601546 - in /logging/log4j/log4j2/trunk: log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/ log4j-core/src/test/java/org/apache/logging/log4j/core/appender/routing/ log4j-core/src/test/java/org/apache/logging/log4j/core/config...
Date Tue, 10 Jun 2014 03:12:24 GMT
Author: rgoers
Date: Tue Jun 10 03:12:24 2014
New Revision: 1601546

URL: http://svn.apache.org/r1601546
Log:
LOG4J2-419 - Support default value for missing key in look ups with fallbacking to looking
in the properties map.

Modified:
    logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/StrSubstitutor.java
    logging/log4j/log4j2/trunk/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/routing/RoutingAppenderWithJndiTest.java
    logging/log4j/log4j2/trunk/log4j-core/src/test/java/org/apache/logging/log4j/core/config/xml/XmlLoggerPropsTest.java
    logging/log4j/log4j2/trunk/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/StrSubstitutorTest.java
    logging/log4j/log4j2/trunk/log4j-core/src/test/resources/log4j-loggerprops.xml
    logging/log4j/log4j2/trunk/log4j-core/src/test/resources/log4j-routing-by-jndi.xml
    logging/log4j/log4j2/trunk/src/changes/changes.xml

Modified: logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/StrSubstitutor.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/StrSubstitutor.java?rev=1601546&r1=1601545&r2=1601546&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/StrSubstitutor.java
(original)
+++ logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/StrSubstitutor.java
Tue Jun 10 03:12:24 2014
@@ -63,6 +63,26 @@ import org.apache.logging.log4j.util.Str
  *      The quick brown fox jumped over the lazy dog.
  * </pre>
  * <p>
+ * Also, this class allows to set a default value for unresolved variables.
+ * The default value for a variable can be appended to the variable name after the variable
+ * default value delimiter. The default value of the variable default value delimiter is
':-',
+ * as in bash and other *nix shells, as those are arguably where the default ${} delimiter
set originated.
+ * The variable default value delimiter can be manually set by calling {@link #setValueDelimiterMatcher(StrMatcher)},
+ * {@link #setValueDelimiter(char)} or {@link #setValueDelimiter(String)}.
+ * The following shows an example with varialbe default value settings:
+ * <pre>
+ * Map valuesMap = HashMap();
+ * valuesMap.put(&quot;animal&quot;, &quot;quick brown fox&quot;);
+ * valuesMap.put(&quot;target&quot;, &quot;lazy dog&quot;);
+ * String templateString = &quot;The ${animal} jumped over the ${target}. ${undefined.number:-1234567890}.&quot;;
+ * StrSubstitutor sub = new StrSubstitutor(valuesMap);
+ * String resolvedString = sub.replace(templateString);
+ * </pre>
+ * yielding:
+ * <pre>
+ *      The quick brown fox jumped over the lazy dog. 1234567890.
+ * </pre>
+ * <p>
  * In addition to this usage pattern there are some static convenience methods that
  * cover the most common use cases. These methods can be used without the need of
  * manually creating an instance. However if multiple replace operations are to be
@@ -116,6 +136,10 @@ public class StrSubstitutor {
      * Constant for the default variable suffix.
      */
     public static final StrMatcher DEFAULT_SUFFIX = StrMatcher.stringMatcher("}");
+    /**
+     * Constant for the default value delimiter of a variable.
+     */
+    public static final StrMatcher DEFAULT_VALUE_DELIMITER = StrMatcher.stringMatcher(":-");
 
     private static final int BUF_SIZE = 256;
 
@@ -132,6 +156,10 @@ public class StrSubstitutor {
      */
     private StrMatcher suffixMatcher;
     /**
+     * Stores the default variable value delimiter
+     */
+    private StrMatcher valueDelimiterMatcher;
+    /**
      * Variable resolution is delegated to an implementer of VariableResolver.
      */
     private StrLookup variableResolver;
@@ -187,6 +215,21 @@ public class StrSubstitutor {
     /**
      * Creates a new instance and initializes it.
      *
+     * @param valueMap  the map with the variables' values, may be null
+     * @param prefix  the prefix for variables, not null
+     * @param suffix  the suffix for variables, not null
+     * @param escape  the escape character
+     * @param valueDelimiter  the variable default value delimiter, may be null
+     * @throws IllegalArgumentException if the prefix or suffix is null
+     */
+    public StrSubstitutor(final Map<String, String> valueMap, final String prefix,
final String suffix, 
+                              final char escape, final String valueDelimiter) {
+        this(new MapLookup(valueMap), prefix, suffix, escape, valueDelimiter);
+    }
+
+    /**
+     * Creates a new instance and initializes it.
+     *
      * @param variableResolver  the variable resolver, may be null
      */
     public StrSubstitutor(final StrLookup variableResolver) {
@@ -214,6 +257,24 @@ public class StrSubstitutor {
      * Creates a new instance and initializes it.
      *
      * @param variableResolver  the variable resolver, may be null
+     * @param prefix  the prefix for variables, not null
+     * @param suffix  the suffix for variables, not null
+     * @param escape  the escape character
+     * @param valueDelimiter  the variable default value delimiter string, may be null
+     * @throws IllegalArgumentException if the prefix or suffix is null
+     */
+    public StrSubstitutor(final StrLookup variableResolver, final String prefix, final String
suffix, final char escape, final String valueDelimiter) {
+        this.setVariableResolver(variableResolver);
+        this.setVariablePrefix(prefix);
+        this.setVariableSuffix(suffix);
+        this.setEscapeChar(escape);
+        this.setValueDelimiter(valueDelimiter);
+    }
+
+    /**
+     * Creates a new instance and initializes it.
+     *
+     * @param variableResolver  the variable resolver, may be null
      * @param prefixMatcher  the prefix for variables, not null
      * @param suffixMatcher  the suffix for variables, not null
      * @param escape  the escape character
@@ -222,11 +283,28 @@ public class StrSubstitutor {
     public StrSubstitutor(final StrLookup variableResolver, final StrMatcher prefixMatcher,
                           final StrMatcher suffixMatcher,
                           final char escape) {
+        this(variableResolver, prefixMatcher, suffixMatcher, escape, DEFAULT_VALUE_DELIMITER);
+    }
+
+    /**
+     * Creates a new instance and initializes it.
+     *
+     * @param variableResolver  the variable resolver, may be null
+     * @param prefixMatcher  the prefix for variables, not null
+     * @param suffixMatcher  the suffix for variables, not null
+     * @param escape  the escape character
+     * @param valueDelimiterMatcher  the variable default value delimiter matcher, may be
null
+     * @throws IllegalArgumentException if the prefix or suffix is null
+     */
+    public StrSubstitutor(
+            final StrLookup variableResolver, final StrMatcher prefixMatcher, final StrMatcher
suffixMatcher, final char escape, final StrMatcher valueDelimiterMatcher) {
         this.setVariableResolver(variableResolver);
         this.setVariablePrefixMatcher(prefixMatcher);
         this.setVariableSuffixMatcher(suffixMatcher);
         this.setEscapeChar(escape);
+        this.setValueDelimiterMatcher(valueDelimiterMatcher);
     }
+
     //-----------------------------------------------------------------------
     /**
      * Replaces all the occurrences of variables in the given source object with
@@ -756,6 +834,8 @@ public class StrSubstitutor {
         final StrMatcher prefixMatcher = getVariablePrefixMatcher();
         final StrMatcher suffixMatcher = getVariableSuffixMatcher();
         final char escape = getEscapeChar();
+        final StrMatcher valueDelimiterMatcher = getValueDelimiterMatcher();
+        final boolean substitutionInVariablesEnabled = isEnableSubstitutionInVariables();
 
         final boolean top = (priorVariables == null);
         boolean altered = false;
@@ -784,7 +864,7 @@ public class StrSubstitutor {
                     int endMatchLen = 0;
                     int nestedVarCount = 0;
                     while (pos < bufEnd) {
-                        if (isEnableSubstitutionInVariables()
+                        if (substitutionInVariablesEnabled
                                 && (endMatchLen = prefixMatcher.isMatch(chars,
                                         pos, offset, bufEnd)) != 0) {
                             // found a nested variable start
@@ -800,17 +880,37 @@ public class StrSubstitutor {
                         } else {
                             // found variable end marker
                             if (nestedVarCount == 0) {
-                                String varName = new String(chars, startPos
+                                String varNameExpr = new String(chars, startPos
                                         + startMatchLen, pos - startPos
                                         - startMatchLen);
-                                if (isEnableSubstitutionInVariables()) {
-                                    final StringBuilder bufName = new StringBuilder(varName);
+                                if (substitutionInVariablesEnabled) {
+                                    final StringBuilder bufName = new StringBuilder(varNameExpr);
                                     substitute(event, bufName, 0, bufName.length());
-                                    varName = bufName.toString();
+                                    varNameExpr = bufName.toString();
                                 }
                                 pos += endMatchLen;
                                 final int endPos = pos;
 
+                                String varName = varNameExpr;
+                                String varDefaultValue = null;
+
+                                if (valueDelimiterMatcher != null) {
+                                    final char [] varNameExprChars = varNameExpr.toCharArray();
+                                    int valueDelimiterMatchLen = 0;
+                                    for (int i = 0; i < varNameExprChars.length; i++)
{
+                                        // if there's any nested variable when nested variable
substitution disabled, then stop resolving name and default value.
+                                        if (!substitutionInVariablesEnabled
+                                                && prefixMatcher.isMatch(varNameExprChars,
i, i, varNameExprChars.length) != 0) {
+                                            break;
+                                        }
+                                        if ((valueDelimiterMatchLen = valueDelimiterMatcher.isMatch(varNameExprChars,
i)) != 0) {
+                                            varName = varNameExpr.substring(0, i);
+                                            varDefaultValue = varNameExpr.substring(i + valueDelimiterMatchLen);
+                                            break;
+                                        }
+                                    }
+                                }
+
                                 // on the first call initialize priorVariables
                                 if (priorVariables == null) {
                                     priorVariables = new ArrayList<String>();
@@ -823,8 +923,11 @@ public class StrSubstitutor {
                                 priorVariables.add(varName);
 
                                 // resolve the variable
-                                final String varValue = resolveVariable(event, varName, buf,
+                                String varValue = resolveVariable(event, varName, buf,
                                         startPos, endPos);
+                                if (varValue == null) {
+                                    varValue = varDefaultValue;
+                                }
                                 if (varValue != null) {
                                     // recursive replace
                                     final int varLen = varValue.length();
@@ -1057,6 +1160,76 @@ public class StrSubstitutor {
         return setVariableSuffixMatcher(StrMatcher.stringMatcher(suffix));
     }
 
+    // Variable Default Value Delimiter
+    //-----------------------------------------------------------------------
+    /**
+     * Gets the variable default value delimiter matcher currently in use.
+     * <p>
+     * The variable default value delimiter is the characer or characters that delimite the
+     * variable name and the variable default value. This delimiter is expressed in terms
of a matcher
+     * allowing advanced variable default value delimiter matches.
+     * <p>
+     * If it returns null, then the variable default value resolution is disabled.
+     *
+     * @return the variable default value delimiter matcher in use, may be null
+     */
+    public StrMatcher getValueDelimiterMatcher() {
+        return valueDelimiterMatcher;
+    }
+
+    /**
+     * Sets the variable default value delimiter matcher to use.
+     * <p>
+     * The variable default value delimiter is the characer or characters that delimite the
+     * variable name and the variable default value. This delimiter is expressed in terms
of a matcher
+     * allowing advanced variable default value delimiter matches.
+     * <p>
+     * If the <code>valueDelimiterMatcher</code> is null, then the variable default
value resolution
+     * becomes disabled.
+     *
+     * @param valueDelimiterMatcher  variable default value delimiter matcher to use, may
be null
+     * @return this, to enable chaining
+     */
+    public StrSubstitutor setValueDelimiterMatcher(final StrMatcher valueDelimiterMatcher)
{
+        this.valueDelimiterMatcher = valueDelimiterMatcher;
+        return this;
+    }
+
+    /**
+     * Sets the variable default value delimiter to use.
+     * <p>
+     * The variable default value delimiter is the characer or characters that delimite the
+     * variable name and the variable default value. This method allows a single character
+     * variable default value delimiter to be easily set.
+     *
+     * @param valueDelimiter  the variable default value delimiter character to use
+     * @return this, to enable chaining
+     */
+    public StrSubstitutor setValueDelimiter(final char valueDelimiter) {
+        return setValueDelimiterMatcher(StrMatcher.charMatcher(valueDelimiter));
+    }
+
+    /**
+     * Sets the variable default value delimiter to use.
+     * <p>
+     * The variable default value delimiter is the characer or characters that delimite the
+     * variable name and the variable default value. This method allows a string
+     * variable default value delimiter to be easily set.
+     * <p>
+     * If the <code>valueDelimiter</code> is null or empty string, then the variable
default
+     * value resolution becomes disabled.
+     *
+     * @param valueDelimiter  the variable default value delimiter string to use, may be
null or empty
+     * @return this, to enable chaining
+     */
+    public StrSubstitutor setValueDelimiter(final String valueDelimiter) {
+        if (Strings.isEmpty(valueDelimiter)) {
+            setValueDelimiterMatcher(null);
+            return this;
+        }
+        return setValueDelimiterMatcher(StrMatcher.stringMatcher(valueDelimiter));
+    }
+
     // Resolver
     //-----------------------------------------------------------------------
     /**

Modified: logging/log4j/log4j2/trunk/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/routing/RoutingAppenderWithJndiTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/routing/RoutingAppenderWithJndiTest.java?rev=1601546&r1=1601545&r2=1601546&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/routing/RoutingAppenderWithJndiTest.java
(original)
+++ logging/log4j/log4j2/trunk/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/routing/RoutingAppenderWithJndiTest.java
Tue Jun 10 03:12:24 2014
@@ -65,7 +65,7 @@ public class RoutingAppenderWithJndiTest
         // default route when there's no jndi resource
         StructuredDataMessage msg = new StructuredDataMessage("Test", "This is a message
from unknown context", "Context");
         EventLogger.logEvent(msg);
-        File defaultLogFile = new File("target/routingbyjndi/routingbyjnditest-default.log");
+        File defaultLogFile = new File("target/routingbyjndi/routingbyjnditest-unknown.log");
         assertTrue("The default log file was not created", defaultLogFile.exists());
 
         // now set jndi resource to Application1
@@ -91,5 +91,21 @@ public class RoutingAppenderWithJndiTest
         assertNotNull("No events generated", listAppender2.getEvents());
         assertTrue("Incorrect number of events. Expected 2, got " + listAppender2.getEvents().size(),
listAppender2.getEvents().size() == 2);
         assertTrue("Incorrect number of events. Expected 1, got " + listAppender1.getEvents().size(),
listAppender1.getEvents().size() == 1);
+
+        // now set jndi resource to Application3.
+        // The context name, 'Application3', will be used as log file name by the default
route.
+        context.rebind("java:comp/env/logging/context-name", "Application3");
+        msg = new StructuredDataMessage("Test", "This is a message from Application3", "Context");
+        EventLogger.logEvent(msg);
+        File application3LogFile = new File("target/routingbyjndi/routingbyjnditest-Application3.log");
+        assertTrue("The Application3 log file was not created", application3LogFile.exists());
+
+        // now set jndi resource to Application4
+        // The context name, 'Application4', will be used as log file name by the default
route.
+        context.rebind("java:comp/env/logging/context-name", "Application4");
+        msg = new StructuredDataMessage("Test", "This is a message from Application4", "Context");
+        EventLogger.logEvent(msg);
+        File application4LogFile = new File("target/routingbyjndi/routingbyjnditest-Application4.log");
+        assertTrue("The Application3 log file was not created", application4LogFile.exists());
     }
 }

Modified: logging/log4j/log4j2/trunk/log4j-core/src/test/java/org/apache/logging/log4j/core/config/xml/XmlLoggerPropsTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/test/java/org/apache/logging/log4j/core/config/xml/XmlLoggerPropsTest.java?rev=1601546&r1=1601545&r2=1601546&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-core/src/test/java/org/apache/logging/log4j/core/config/xml/XmlLoggerPropsTest.java
(original)
+++ logging/log4j/log4j2/trunk/log4j-core/src/test/java/org/apache/logging/log4j/core/config/xml/XmlLoggerPropsTest.java
Tue Jun 10 03:12:24 2014
@@ -17,11 +17,12 @@
 package org.apache.logging.log4j.core.config.xml;
 
 import java.util.List;
-import java.util.Map;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.core.Appender;
 import org.apache.logging.log4j.core.LoggerContext;
 import org.apache.logging.log4j.core.config.Configuration;
 import org.apache.logging.log4j.core.config.ConfigurationFactory;
@@ -72,7 +73,16 @@ public class XmlLoggerPropsTest {
             final List<String> events = listAppender.getMessages();
             assertTrue("No events", events.size() > 0);
             assertTrue("Incorrect number of events", events.size() == 2);
+            assertTrue("Incorrect value", events.get(0).contains("user="));
+            assertTrue("Incorrect value", events.get(0).contains("phrasex=****"));
             assertTrue("Incorrect value", events.get(0).contains("test=test"));
+            assertTrue("Incorrect value", events.get(0).contains("test2=test2default"));
+            assertTrue("Incorrect value", events.get(0).contains("test3=Unknown"));
+            assertTrue("Incorrect value", events.get(1).contains("user="));
+            assertTrue("Incorrect value", events.get(1).contains("phrasex=****"));
+            assertTrue("Incorrect value", events.get(1).contains("test=test"));
+            assertTrue("Incorrect value", events.get(1).contains("test2=test2default"));
+            assertTrue("Incorrect value", events.get(1).contains("test3=Unknown"));
         } finally {
             System.clearProperty("test");
         }

Modified: logging/log4j/log4j2/trunk/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/StrSubstitutorTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/StrSubstitutorTest.java?rev=1601546&r1=1601545&r2=1601546&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/StrSubstitutorTest.java
(original)
+++ logging/log4j/log4j2/trunk/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/StrSubstitutorTest.java
Tue Jun 10 03:12:24 2014
@@ -31,7 +31,7 @@ import static org.junit.Assert.*;
  */
 public class StrSubstitutorTest {
 
-     private static final String TESTKEY = "TestKey";
+    private static final String TESTKEY = "TestKey";
     private static final String TESTVAL = "TestValue";
 
 
@@ -57,5 +57,12 @@ public class StrSubstitutorTest {
         assertEquals("TestValue-TestValue-TestValue", value);
         value = subst.replace("${BadKey}");
         assertEquals("${BadKey}", value);
+
+        value = subst.replace("${BadKey:-Unknown}-${ctx:BadKey:-Unknown}-${sys:BadKey:-Unknown}");
+        assertEquals("Unknown-Unknown-Unknown", value);
+        value = subst.replace("${BadKey:-Unknown}-${ctx:BadKey}-${sys:BadKey:-Unknown}");
+        assertEquals("Unknown-${ctx:BadKey}-Unknown", value);
+        value = subst.replace("${BadKey:-Unknown}-${ctx:BadKey:-}-${sys:BadKey:-Unknown}");
+        assertEquals("Unknown--Unknown", value);
     }
 }

Modified: logging/log4j/log4j2/trunk/log4j-core/src/test/resources/log4j-loggerprops.xml
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/test/resources/log4j-loggerprops.xml?rev=1601546&r1=1601545&r2=1601546&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-core/src/test/resources/log4j-loggerprops.xml (original)
+++ logging/log4j/log4j2/trunk/log4j-core/src/test/resources/log4j-loggerprops.xml Tue Jun
10 03:12:24 2014
@@ -16,20 +16,25 @@
   ~ limitations under the License.
   -->
 <Configuration status="OFF" strict="false" name="DSI">
+  <Properties>
+     <Property name="test2">test2default</Property>
+  </Properties>
   <Appenders>
     <List name="List">
-      <PatternLayout pattern="[%-5level] %c{1.} user=%X{user} test=%X{test} %msg%n" />
+        <PatternLayout pattern="[%-5level] %c{1.} user=%X{user} phrasex=%X{phrasex} test=%X{test}
test2=$${sys:test2} test3=$${sys:test3:-Unknown} %msg%n" />
     </List>
   </Appenders>
 
   <Loggers>
     <Logger name="org.apache.logging.log4j.core" level="debug" additivity="false">
       <Property name="user">$${sys:user.name}</Property>
+      <Property name="phrasex">${sys:user.phrasex:-****}</Property>
       <Property name="test">${sys:test}</Property>
       <AppenderRef ref="List"/>
     </Logger>
     <Root level="debug">
       <Property name="user">${sys:user.name}</Property>
+      <Property name="phrasex">${sys:user.phrasex:-****}</Property>
       <Property name="test">${sys:test}</Property>
       <AppenderRef ref="List" />
     </Root>

Modified: logging/log4j/log4j2/trunk/log4j-core/src/test/resources/log4j-routing-by-jndi.xml
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/test/resources/log4j-routing-by-jndi.xml?rev=1601546&r1=1601545&r2=1601546&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-core/src/test/resources/log4j-routing-by-jndi.xml (original)
+++ logging/log4j/log4j2/trunk/log4j-core/src/test/resources/log4j-routing-by-jndi.xml Tue
Jun 10 03:12:24 2014
@@ -32,9 +32,8 @@
     </List>
     <Routing name="Routing">
       <Routes pattern="$${jndi:logging/context-name}">
-        <!-- Default route -->
         <Route>
-          <File name="DefaultApplicationLogFile" fileName="target/routingbyjndi/routingbyjnditest-default.log"
append="false">
+          <File name="DefaultApplicationLogFile" fileName="target/routingbyjndi/routingbyjnditest-${jndi:logging/context-name:-unknown}.log"
append="false">
             <PatternLayout>
               <Pattern>%d %p %C{1.} [%t] %m%n</Pattern>
             </PatternLayout>

Modified: logging/log4j/log4j2/trunk/src/changes/changes.xml
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/src/changes/changes.xml?rev=1601546&r1=1601545&r2=1601546&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/src/changes/changes.xml (original)
+++ logging/log4j/log4j2/trunk/src/changes/changes.xml Tue Jun 10 03:12:24 2014
@@ -22,6 +22,9 @@
   </properties>
   <body>
     <release version="2.0-rc2" date="2014-MM-DD" description="Bug fixes and enhancements">
+      <action issue="LOG4J2-419" dev="rgoers" type="update" due-to="Woonsan Ko">
+          Support default value for missing key in look ups with fallbacking to looking in
the properties map.
+      </action>
       <action issue="LOG4J2-563" dev="rgoers" type="fix" due-to="Michael Friedmann">
         FlumeAvroManager now always uses a client type of default_failover.
       </action>



Mime
View raw message