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 38b3eec734 LOG4J2-2785 - Pattern Layout to abbreviate the name of all 
logger components except the rightmost words
38b3eec734 is described below

commit 38b3eec734438ec90c7f75224fbf8348be7f6d68
Author: Ralph Goers <rgo...@apache.org>
AuthorDate: Fri Dec 30 12:19:15 2022 -0700

    LOG4J2-2785 - Pattern Layout to abbreviate the name of all logger 
components except the rightmost words
---
 .../core/pattern/DynamicWordAbbreviatorTest.java   |  75 +++++++++++++
 .../log4j/core/pattern/NameAbbreviatorTest.java    |   5 +-
 .../log4j/core/pattern/DynamicWordAbbreviator.java | 118 +++++++++++++++++++++
 .../log4j/core/pattern/NameAbbreviator.java        |  17 +++
 src/changes/changes.xml                            |   6 ++
 src/site/asciidoc/manual/layouts.adoc              |  24 ++++-
 6 files changed, 242 insertions(+), 3 deletions(-)

diff --git 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/DynamicWordAbbreviatorTest.java
 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/DynamicWordAbbreviatorTest.java
new file mode 100644
index 0000000000..e52d20088f
--- /dev/null
+++ 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/DynamicWordAbbreviatorTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+import org.junit.jupiter.params.provider.ValueSource;
+
+/**
+ * Unit tests for the {@link DynamicWordAbbreviator} class.
+ */
+class DynamicWordAbbreviatorTest extends Assertions {
+
+    @Test
+    void testNullAndEmptyInputs() {
+        DynamicWordAbbreviator abbreviator = 
DynamicWordAbbreviator.create("1.1*");
+
+        assertDoesNotThrow(() -> abbreviator.abbreviate("orig", null));
+        assertDoesNotThrow(() -> abbreviator.abbreviate(null, new 
StringBuilder()));
+
+        StringBuilder dest = new StringBuilder();
+        abbreviator.abbreviate(null, dest);
+        assertEquals("", dest.toString());
+
+        abbreviator.abbreviate("", dest);
+        assertEquals("", dest.toString());
+    }
+
+    @ParameterizedTest(name = "[{index}] \"{0}\"")
+    @ValueSource(strings = {
+            "",
+            " ",
+            "0.0*",
+            "0,0*",
+            "1.2",
+            "1.2**",
+            "1.0*"
+    })
+    void testInvalidPatterns(String pattern) {
+        assertNull(DynamicWordAbbreviator.create(pattern));
+    }
+
+    @ParameterizedTest(name = "[{index}] \"{0}\" \"{1}\" \"{2}\"")
+    @CsvSource(delimiter = '|', value = {
+            "1.1*|.|.",
+            "1.1*|\\ |\\ ",
+            "1.1*|org.novice.o|o.n.o",
+            "1.1*|org.novice.|o.novice",
+            "1.1*|org......novice|o.novice",
+            "1.1*|org. . .novice|o. . .novice",
+    })
+    void testStrangeWords(String pattern, String input, String expected) {
+        DynamicWordAbbreviator abbreviator = 
DynamicWordAbbreviator.create(pattern);
+        StringBuilder actual = new StringBuilder();
+        abbreviator.abbreviate(input, actual);
+        assertEquals(expected, actual.toString());
+    }
+
+}
diff --git 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/NameAbbreviatorTest.java
 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/NameAbbreviatorTest.java
index d49a6f39a2..8e728a6b98 100644
--- 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/NameAbbreviatorTest.java
+++ 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/NameAbbreviatorTest.java
@@ -51,7 +51,10 @@ public class NameAbbreviatorTest {
                 { "1.", "o.a.l.l.c.p.NameAbbreviatorTest" },
                 { "1.1.~", "o.a.~.~.~.~.NameAbbreviatorTest" },
                 { "1.1.1.*", "o.a.l.log4j.core.pattern.NameAbbreviatorTest" },
-                { ".", "......NameAbbreviatorTest" }
+                { ".", "......NameAbbreviatorTest" },
+                { "1.2*", "o.a.l.l.c.pattern.NameAbbreviatorTest" },
+                { "1.3*", "o.a.l.l.core.pattern.NameAbbreviatorTest" },
+                { "1.8*", 
"org.apache.logging.log4j.core.pattern.NameAbbreviatorTest" }
             }
         );
     }
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/DynamicWordAbbreviator.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/DynamicWordAbbreviator.java
new file mode 100644
index 0000000000..305731e93a
--- /dev/null
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/DynamicWordAbbreviator.java
@@ -0,0 +1,118 @@
+/*
+ * 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 java.util.Arrays;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * <p>Specialized abbreviator that shortens all words to the first char except 
the indicated number of rightmost words.
+ * To select this abbreviator, use pattern <code>1.n*</code> where n (&gt; 0) 
is the number of rightmost words to leave unchanged.</p>
+ *
+ * By example for input 
<code>org.apache.logging.log4j.core.pattern.NameAbbreviator</code>:<br>
+ * <pre>
+ * 1.1*     =&gt;   o.a.l.l.c.p.NameAbbreviator
+ * 1.2*     =&gt;   o.a.l.l.c.pattern.NameAbbreviator
+ * 1.3*     =&gt;   o.a.l.l.core.pattern.NameAbbreviator
+ * ..
+ * 1.999*   =&gt;   org.apache.logging.log4j.core.pattern.NameAbbreviator
+ * </pre>
+ * @since 2.19.1
+ */
+class DynamicWordAbbreviator extends NameAbbreviator {
+
+    /** Right-most number of words (at least one) that will not be 
abbreviated. */
+    private final int rightWordCount;
+
+    static DynamicWordAbbreviator create(String pattern) {
+        if (pattern != null) {
+            Matcher matcher = 
Pattern.compile("1\\.([1-9][0-9]*)\\*").matcher(pattern);
+            if (matcher.matches()) {
+                return new 
DynamicWordAbbreviator(Integer.parseInt(matcher.group(1)));
+            }
+        }
+        return null;
+    }
+
+    private DynamicWordAbbreviator(int rightWordCount) {
+        this.rightWordCount = rightWordCount;
+    }
+
+    @Override
+    public void abbreviate(final String original, final StringBuilder 
destination) {
+        if (original == null || destination == null) {
+            return;
+        }
+
+        // for efficiency refrain from using String#split or StringTokenizer
+        final String[] words = split(original, '.');
+        final int wordCount = words.length;
+
+        if (rightWordCount >= wordCount) {
+            // nothing to abbreviate
+            destination.append(original);
+            return;
+        }
+
+        final int lastAbbrevIdx = wordCount - rightWordCount; // last index to 
abbreviate
+        for (int i = 0; i < wordCount; i++) {
+            if (i >= lastAbbrevIdx) {
+                destination.append(words[i]);
+                if (i < wordCount - 1) {
+                    destination.append(".");
+                }
+            } else if (words[i].length() > 0) {
+                destination.append(words[i].charAt(0))
+                        .append(".");
+            }
+        }
+    }
+
+    static String[] split(final String input, final char delim) {
+        if (input == null) {
+            return null;
+        } else if (input.isEmpty()) {
+            return new String[0];
+        }
+
+        int countDelim = input.chars().filter(c -> c == delim).map(c -> 
1).sum();
+        final String[] tokens = new String[countDelim + 1];
+
+        int countToken = 0;
+        int idxBegin = 0;
+        int idxDelim = 0;
+
+        while ((idxDelim = input.indexOf(delim, idxBegin)) > -1) {
+            if (idxBegin < idxDelim) {
+                tokens[countToken++] = input.substring(idxBegin, idxDelim);
+            }
+            idxBegin = idxDelim + 1;
+        }
+
+        if (idxBegin < input.length()) { // remains
+            tokens[countToken++] = input.substring(idxBegin);
+        }
+
+        if (countToken < tokens.length) {
+            return Arrays.copyOf(tokens, countToken);
+        }
+
+        return tokens;
+    }
+
+}
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/NameAbbreviator.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/NameAbbreviator.java
index 02997ed046..331ad3931d 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/NameAbbreviator.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/NameAbbreviator.java
@@ -17,6 +17,7 @@
 package org.apache.logging.log4j.core.pattern;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 import org.apache.logging.log4j.util.PerformanceSensitive;
@@ -55,6 +56,11 @@ public abstract class NameAbbreviator {
                 return DEFAULT;
             }
 
+            NameAbbreviator dwa = DynamicWordAbbreviator.create(trimmed);
+            if (dwa != null) {
+                return dwa;
+            }
+
             final boolean isNegativeNumber;
             final String number;
 
@@ -319,6 +325,12 @@ public abstract class NameAbbreviator {
             }
             return nextDot + 1;
         }
+
+        @Override
+        public String toString() {
+            return String.format("%s[charCount=%s, ellipsis=%s]",
+                    getClass().getSimpleName(), charCount, 
Integer.toHexString(ellipsis));
+        }
     }
 
     /**
@@ -364,5 +376,10 @@ public abstract class NameAbbreviator {
         PatternAbbreviatorFragment fragment(int index) {
             return fragments[Math.min(index, fragments.length - 1)];
         }
+
+        @Override
+        public String toString() {
+            return String.format("%s[fragments=%s]", 
getClass().getSimpleName(), Arrays.toString(fragments));
+        }
     }
 }
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index bacbeb82a7..f3d8ba836a 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -31,6 +31,9 @@
          - "remove" - Removed
     -->
     <release version="3.0.0" date="2022-MM-DD" description="GA Release 3.0.0">
+      <action issue="LOG4J2-3644" dev="rgoers" type="update">
+        Remove support for package scanning for plugins.
+      </action>
       <action issue="LOG4J2-3626" dev="mattsicker" type="update">
         Flatten the ThreadContextMap interfaces with default methods.
       </action>
@@ -199,6 +202,9 @@
       </action>
     </release>
     <release version="2.19.1" date="TBD" description="GA Release 2.19.1">
+      <action issue="LOG4J2-2785" dev="rgoers" type="add" due-to="Markus 
Spann">
+        Pattern Layout to abbreviate the name of all logger components except 
the 2 rightmost.
+      </action>
       <action issue="LOG4J2-2678" dev="pkarwasz" type="update" 
due-to="Federico D'Ambrosio">
         Add LogEvent timestamp to ProducerRecord in KafkaAppender.
       </action>
diff --git a/src/site/asciidoc/manual/layouts.adoc 
b/src/site/asciidoc/manual/layouts.adoc
index 6f40d444a2..659eab8c85 100644
--- a/src/site/asciidoc/manual/layouts.adoc
+++ b/src/site/asciidoc/manual/layouts.adoc
@@ -808,7 +808,10 @@ When the precision specifier is an integer value, it 
reduces the size of
 the logger name. If the number is positive, the layout prints the
 corresponding number of rightmost logger name components. If negative,
 the layout removes the corresponding number of leftmost logger name
-components.
+components. If the precision contains periods then the number before the first 
period
+identifies the length to be printed from items that precede tokens in the rest 
of the pattern.
+If the number after the first period is followed by an asterisk it indicates 
how many of the
+rightmost tokens will be printed in full. See the table below for abbreviation 
examples.
 
 If the precision contains any non-integer characters, then the layout
 abbreviates the name based on the pattern. If the precision integer is
@@ -846,13 +849,30 @@ default, the layout prints the logger name in full.
 !org.apache.commons.Foo
 !o.a.c.Foo
 
-!%c{1.1.~.~}
+!%c{1.1.\~.~}
 !org.apache.commons.test.Foo
 !o.a.~.~.Foo
 
 !%c{.}
 !org.apache.commons.test.Foo
 !....Foo
+
+!%c{1.1.1.*}
+!org.apache.commons.test.Foo
+!o.a.c.test.Foo
+
+!%c{1.2.*}
+!org.apache.commons.test.Foo
+!o.a.c.test.Foo
+
+!%c{1.3.*}
+!org.apache.commons.test.Foo
+!o.a.commons.test.Foo
+
+!%c{1.8.*}
+!org.apache.commons.test.Foo
+!org.apache.commons.test.Foo
+
 !===
 
 |[[PatternClass]] *C*{precision} +

Reply via email to