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:36 UTC

[logging-log4j2] branch master 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 master
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git


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

commit 19aa7fd40606d728471953890f86c9ae4d88ea5b
Author: Ralph Goers <rg...@apache.org>
AuthorDate: Sun Feb 16 00:38:18 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/asciidoc/manual/configuration.adoc        |  6 ++
 src/site/asciidoc/manual/lookups.adoc              | 13 +++-
 8 files changed, 154 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 7003e3f..446d57b 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 adddd53..df8d8a4 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 6150afd..ddb8b8f 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 2b02b02..24d73f0 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -166,6 +166,9 @@
       </action>
     </release>
     <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/asciidoc/manual/configuration.adoc b/src/site/asciidoc/manual/configuration.adoc
index 0a466c1..82c4a3e 100644
--- a/src/site/asciidoc/manual/configuration.adoc
+++ b/src/site/asciidoc/manual/configuration.adoc
@@ -1127,6 +1127,12 @@ for "hostName" that is the current system's host name or IP address and
 the "contextName" with is the value of the current logging context. See many places
 a Properties element is used in this section for examples.
 
+Default properties may also be specified in the Lookup by using the syntax `${lookupName:key:-defaultValue}`.
+In some cases the key might contain a leading '-'. When this is the case an escape character must be
+included, such as ``${main:\--file:-app.properties}`. This would use the
+`MainMapLookup` for a key named `--file`. If the key is not found then
+<code>app.properties</code> would be used as the default value.
+
 [#RuntimeLookup]
 == Lookup Variables with Multiple Leading '$' Characters
 
diff --git a/src/site/asciidoc/manual/lookups.adoc b/src/site/asciidoc/manual/lookups.adoc
index 5585b1a..1efc99a 100644
--- a/src/site/asciidoc/manual/lookups.adoc
+++ b/src/site/asciidoc/manual/lookups.adoc
@@ -331,6 +331,11 @@ configuration. The key that follows the `main:` prefix can either be a
 `${main:myString}` is substituted with the value that follows `myString`
 in the main argument list.
 
+Note: Many applications use leading dashes to identify command arguments. Specifying
+`${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 as in `${main:\--file}`.
+
 For example, suppose the static void main String[] arguments are:
 
 ....
@@ -358,16 +363,20 @@ Then the following substitutions are possible:
 |${main:4}
 |bar
 
-|${main:--file}
+|${main:\--file}
 |foo.txt
 
-|${main:-x}
+|${main:\-x}
 |bar
 
 |${main:bar}
 |null
 |===
 
+|${main:\--quiet:-true}
+|true
+|===
+
 Example usage:
 
 [source,xml]