You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rg...@apache.org on 2020/02/16 07:38:52 UTC

[logging-log4j2] branch release-2.x updated: LOG4J2-2211 - Allow Lookup keys with leading dashes by using a slash as an escape character.

This is an automated email from the ASF dual-hosted git repository.

rgoers pushed a commit to branch release-2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git


The following commit(s) were added to refs/heads/release-2.x by this push:
     new bb17938  LOG4J2-2211 - Allow Lookup keys with leading dashes by using a slash as an escape character.
bb17938 is described below

commit bb1793874d3fada1c55dfe265e7a9d7ed4aa4c74
Author: Ralph Goers <rg...@apache.org>
AuthorDate: Sun Feb 16 00:38:41 2020 -0700

    LOG4J2-2211 - Allow Lookup keys with leading dashes by using a slash as an escape character.
---
 .../logging/log4j/core/lookup/Interpolator.java    |  6 +-
 .../logging/log4j/core/lookup/MainMapLookup.java   |  8 ++-
 .../logging/log4j/core/lookup/StrSubstitutor.java  | 75 +++++++++++++++++++---
 ...pTest.java => MainInputArgumentsLookupApp.java} |  4 +-
 .../logging/log4j/core/lookup/MainLookupTest.java  | 56 ++++++++++++++++
 src/changes/changes.xml                            |  3 +
 src/site/xdoc/manual/configuration.xml.vm          |  7 ++
 src/site/xdoc/manual/lookups.xml                   | 14 +++-
 8 files changed, 156 insertions(+), 17 deletions(-)

diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/Interpolator.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/Interpolator.java
index 328b21a..f0c40f5 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/Interpolator.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/Interpolator.java
@@ -36,6 +36,9 @@ import org.apache.logging.log4j.util.Constants;
  */
 public class Interpolator extends AbstractConfigurationAwareLookup {
 
+    /** Constant for the prefix separator. */
+    public static final char PREFIX_SEPARATOR = ':';
+
     private static final String LOOKUP_KEY_WEB = "web";
 
     private static final String LOOKUP_KEY_DOCKER = "docker";
@@ -50,9 +53,6 @@ public class Interpolator extends AbstractConfigurationAwareLookup {
 
     private static final Logger LOGGER = StatusLogger.getLogger();
 
-    /** Constant for the prefix separator. */
-    private static final char PREFIX_SEPARATOR = ':';
-
     private final Map<String, StrLookup> strLookupMap = new HashMap<>();
 
     private final StrLookup defaultLookup;
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/MainMapLookup.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/MainMapLookup.java
index 34b2fd3..d90241e 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/MainMapLookup.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/MainMapLookup.java
@@ -64,9 +64,13 @@ public class MainMapLookup extends MapLookup {
      * Second using the argument at position n as the key to access the value at n+1.
      * </p>
      * <ul>
-     * <li>{@code "main:--file"} = {@code "path/file.txt"}</li>
-     * <li>{@code "main:-x"} = {@code "2"}</li>
+     * <li>{@code "main:\--file"} = {@code "path/file.txt"}</li>
+     * <li>{@code "main:\-x"} = {@code "2"}</li>
      * </ul>
+     *<p>Note: Many applications use leading dashes to identify command arguments. Specifying {@code "main:--file}
+     * would result in the lookup failing because it would look for a variable named "main" with a default
+     * value of "-file". To avoid this the ":" separating the Lookup name from the key must be followed by
+     * a backslash as an escape character.</p>
      *
      * @param args
      *        An application's {@code public static main(String[])} arguments.
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/StrSubstitutor.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/StrSubstitutor.java
index 405ca28..33fc535 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/StrSubstitutor.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/StrSubstitutor.java
@@ -158,7 +158,11 @@ public class StrSubstitutor implements ConfigurationAware {
     /**
      * Constant for the default value delimiter of a variable.
      */
-    public static final StrMatcher DEFAULT_VALUE_DELIMITER = StrMatcher.stringMatcher(":-");
+    public static final String DEFAULT_VALUE_DELIMITER_STRING = ":-";
+    public static final StrMatcher DEFAULT_VALUE_DELIMITER = StrMatcher.stringMatcher(DEFAULT_VALUE_DELIMITER_STRING);
+
+    public static final String ESCAPE_DELIMITER_STRING = ":\\-";
+    public static final StrMatcher DEFAULT_VALUE_ESCAPE_DELIMITER = StrMatcher.stringMatcher(ESCAPE_DELIMITER_STRING);
 
     private static final int BUF_SIZE = 256;
 
@@ -180,9 +184,15 @@ public class StrSubstitutor implements ConfigurationAware {
     /**
      * Stores the default variable value delimiter
      */
+    private String valueDelimiterString;
     private StrMatcher valueDelimiterMatcher;
 
     /**
+     * Escape string to avoid matching the value delimiter matcher;
+     */
+    private StrMatcher valueEscapeDelimiterMatcher;
+
+    /**
      * Variable resolution is delegated to an implementer of VariableResolver.
      */
     private StrLookup variableResolver;
@@ -323,7 +333,28 @@ public class StrSubstitutor implements ConfigurationAware {
     public StrSubstitutor(final StrLookup variableResolver, final StrMatcher prefixMatcher,
                           final StrMatcher suffixMatcher,
                           final char escape) {
-        this(variableResolver, prefixMatcher, suffixMatcher, escape, DEFAULT_VALUE_DELIMITER);
+        this(variableResolver, prefixMatcher, suffixMatcher, escape, DEFAULT_VALUE_DELIMITER,
+                DEFAULT_VALUE_ESCAPE_DELIMITER);
+        this.valueDelimiterString = DEFAULT_VALUE_DELIMITER_STRING;
+    }
+
+    /**
+     * 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);
     }
 
     /**
@@ -334,15 +365,18 @@ public class StrSubstitutor implements ConfigurationAware {
      * @param suffixMatcher  the suffix for variables, not null
      * @param escape  the escape character
      * @param valueDelimiterMatcher  the variable default value delimiter matcher, may be null
+     * @param valueEscapeMatcher the matcher to escape defaulting, 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) {
+    public StrSubstitutor(final StrLookup variableResolver, final StrMatcher prefixMatcher,
+            final StrMatcher suffixMatcher, final char escape, final StrMatcher valueDelimiterMatcher,
+            final StrMatcher valueEscapeMatcher) {
         this.setVariableResolver(variableResolver);
         this.setVariablePrefixMatcher(prefixMatcher);
         this.setVariableSuffixMatcher(suffixMatcher);
         this.setEscapeChar(escape);
         this.setValueDelimiterMatcher(valueDelimiterMatcher);
+        valueEscapeDelimiterMatcher = valueEscapeMatcher;
     }
 
     //-----------------------------------------------------------------------
@@ -960,10 +994,32 @@ public class StrSubstitutor implements ConfigurationAware {
                                                 && 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;
+                                        if (valueEscapeDelimiterMatcher != null) {
+                                            int matchLen = valueEscapeDelimiterMatcher.isMatch(varNameExprChars, i);
+                                            if (matchLen != 0) {
+                                                String varNamePrefix = varNameExpr.substring(0, i) + Interpolator.PREFIX_SEPARATOR;
+                                                varName = varNamePrefix + varNameExpr.substring(i + matchLen - 1);
+                                                for (int j = i + matchLen; j < varNameExprChars.length; ++j){
+                                                    if ((valueDelimiterMatchLen = valueDelimiterMatcher.isMatch(varNameExprChars, j)) != 0) {
+                                                        varName = varNamePrefix + varNameExpr.substring(i + matchLen, j);
+                                                        varDefaultValue = varNameExpr.substring(j + valueDelimiterMatchLen);
+                                                        break;
+                                                    }
+                                                }
+                                                break;
+                                            } else {
+                                                if ((valueDelimiterMatchLen = valueDelimiterMatcher.isMatch(varNameExprChars, i)) != 0) {
+                                                    varName = varNameExpr.substring(0, i);
+                                                    varDefaultValue = varNameExpr.substring(i + valueDelimiterMatchLen);
+                                                    break;
+                                                }
+                                            }
+                                        } else {
+                                            if ((valueDelimiterMatchLen = valueDelimiterMatcher.isMatch(varNameExprChars, i)) != 0) {
+                                                varName = varNameExpr.substring(0, i);
+                                                varDefaultValue = varNameExpr.substring(i + valueDelimiterMatchLen);
+                                                break;
+                                            }
                                         }
                                     }
                                 }
@@ -1294,6 +1350,9 @@ public class StrSubstitutor implements ConfigurationAware {
             setValueDelimiterMatcher(null);
             return this;
         }
+        String escapeValue = valueDelimiter.substring(0, valueDelimiter.length() - 1) + "\\"
+                + valueDelimiter.substring(valueDelimiter.length() - 1);
+        valueEscapeDelimiterMatcher = StrMatcher.stringMatcher(escapeValue);
         return setValueDelimiterMatcher(StrMatcher.stringMatcher(valueDelimiter));
     }
 
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/MainInputArgumentsLookupTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/MainInputArgumentsLookupApp.java
similarity index 94%
rename from log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/MainInputArgumentsLookupTest.java
rename to log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/MainInputArgumentsLookupApp.java
index 966c6fd..44149a8 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/MainInputArgumentsLookupTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/MainInputArgumentsLookupApp.java
@@ -28,11 +28,11 @@ import org.apache.logging.log4j.core.config.Configurator;
  *
  * @since 2.4
  */
-public class MainInputArgumentsLookupTest {
+public class MainInputArgumentsLookupApp {
 
     public static void main(final String[] args) {
         MainMapLookup.setMainArguments(args);
-        try (final LoggerContext ctx = Configurator.initialize(MainInputArgumentsLookupTest.class.getName(),
+        try (final LoggerContext ctx = Configurator.initialize(MainInputArgumentsLookupApp.class.getName(),
                 "target/test-classes/log4j-lookup-main.xml")) {
             LogManager.getLogger().error("this is an error message");
         }
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/MainLookupTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/MainLookupTest.java
new file mode 100644
index 0000000..b019c2a
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/MainLookupTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.lookup;
+
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests MainLookup.
+ */
+public class MainLookupTest {
+
+    @Test
+    public void testMainArgs(){
+        MainMapLookup.setMainArguments("--file", "foo.txt", "--verbose", "-x", "bar");
+        String str ="${key} ${main:-1} ${main:0} ${main:1} ${main:2} ${main:3} ${main:4} ${main:\\--file} ${main:foo.txt} ${main:\\--verbose} ${main:\\-x} ${main:bar} ${main:\\--quiet:-true}";
+        Map<String, String> properties =  new HashMap<String, String>();
+        properties.put("key", "value");
+        properties.put("bar", "default_bar_value");
+        Interpolator lookup = new Interpolator(properties);
+        StrSubstitutor substitutor = new StrSubstitutor(lookup);
+        String replacedValue = substitutor.replace(null, str);
+        String[] values = replacedValue.split(" ");
+        assertEquals("Item 0 is incorrect ", "value", values[0]);
+        assertEquals("Item 1 is incorrect ", "1", values[1]);
+        assertEquals("Item 2 is incorrect", "--file", values[2]);
+        assertEquals("Item 3 is incorrect", "foo.txt", values[3]);
+        assertEquals("Item 4 is incorrect", "--verbose", values[4]);
+        assertEquals("Item 5 is incorrect", "-x", values[5]);
+        assertEquals("Iten 6 is incorrect", "bar", values[6]);
+        assertEquals("Item 7 is incorrect", "foo.txt", values[7]);
+        assertEquals("Item 8 is incorrect", "--verbose", values[8]);
+        assertEquals("Item 9 is incorrect", "-x", values[9]);
+        assertEquals("Item 10 is incorrect", "bar", values[10]);
+        assertEquals("Item 11 is incorrect", "default_bar_value", values[11]);
+        assertEquals("Item 12 is incorrect", "true", values[12]);
+    }
+}
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 72adc8e..cf0f4fd 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -30,6 +30,9 @@
          - "remove" - Removed
     -->
     <release version="2.13.1" date="2019-MM-DD" description="GA Release 2.13.1">
+      <action issue="LOG4J2-2211" dev="rgoers" type="fix">
+        Allow Lookup keys with leading dashes by using a slash as an escape character.
+      </action>
       <action issue="LOG4J2-2782" dev="rgoers" type="update">
         Use LinkedBlockingQueue instead of synchronized collction in StatusConfiguration.
       </action>
diff --git a/src/site/xdoc/manual/configuration.xml.vm b/src/site/xdoc/manual/configuration.xml.vm
index 9fd2e31..a057ab4 100644
--- a/src/site/xdoc/manual/configuration.xml.vm
+++ b/src/site/xdoc/manual/configuration.xml.vm
@@ -1073,6 +1073,13 @@ rootLogger.appenderRef.stdout.ref = STDOUT
           the "contextName" with is the value of the current logging context. See many places
           a Properties element is used in this section for examples.
           </p>
+          <p>
+            Default properties may also be specified in the Lookup by using the syntax <code>${dollar}{lookupName:key:-defaultValue}</code>.
+            In some cases the key might contain a leading '-'. When this is the case an escape character must be
+            included, such as <code>${dollar}{main:\--file:-app.properties}</code>. This would use the
+            <code>MainMapLookup</code> for a key named <code>--file</code>. If the key is not found then
+            <code>app.properties</code> would be used as the default value.
+          </p>
         </subsection>
         <a name="RuntimeLookup"/>
         <subsection name="Lookup Variables with Multiple Leading '$' Characters">
diff --git a/src/site/xdoc/manual/lookups.xml b/src/site/xdoc/manual/lookups.xml
index 40322bd..a5dbca6 100644
--- a/src/site/xdoc/manual/lookups.xml
+++ b/src/site/xdoc/manual/lookups.xml
@@ -315,6 +315,10 @@ public static void main(String args[]) {
           or a string, where <code>${main:myString}</code> is substituted with the value that follows
           <code>myString</code> in the main argument list.
         </p>
+          Note: Many applications use leading dashes to identify command arguments. Specifying
+          <code>${main:--file}</code> would result in the lookup failing because it would look for a variable
+          named "main" with a default value of "-file". To avoid this the ":" separating the Lookup name from the
+          key must be followed by a backslash as an escape character as in <code>${main:\--file}</code>
         <p>
           For example, suppose the static void main String[] arguments are:
         </p>
@@ -358,13 +362,13 @@ public static void main(String args[]) {
             </td>
           </tr>
           <tr>
-            <td>${main:--file}</td>
+            <td>${main:\--file}</td>
             <td>
               <p><code>foo.txt</code></p>
             </td>
           </tr>
           <tr>
-            <td>${main:-x}</td>
+            <td>${main:\-x}</td>
             <td>
               <p><code>bar</code></p>
             </td>
@@ -375,6 +379,12 @@ public static void main(String args[]) {
               <p><code>null</code></p>
             </td>
           </tr>
+          <tr>
+            <td>${main:\--quiet:-true}</td>
+            <td>
+              <p><code>true</code></p>
+            </td>
+          </tr>
         </table>
           <p>
             Example usage: