From 7ebd6b2efa156ef0b95e6848478bff1235066c87 Mon Sep 17 00:00:00 2001 From: Bertrand Renuart Date: Sun, 16 Oct 2022 23:45:51 +0200 Subject: [PATCH] ShortenedThrowableConverter: and are mutually exclusive (#881) * and should ADD to the list instead of replacing content * Also add a common utility method in StringUtils to convert a comma delimited list of strings into an array. * Rename setExclusions() into addExclusions() to better reflect the intend Related to issue #876 --- .../ShortenedThrowableConverter.java | 43 +++--- .../logstash/logback/util/StringUtils.java | 73 ++++++++++ .../ShortenedThrowableConverterTest.java | 2 +- .../logback/util/StringUtilsTest.java | 135 ++++++++++++++++++ 4 files changed, 233 insertions(+), 20 deletions(-) create mode 100644 src/test/java/net/logstash/logback/util/StringUtilsTest.java diff --git a/src/main/java/net/logstash/logback/stacktrace/ShortenedThrowableConverter.java b/src/main/java/net/logstash/logback/stacktrace/ShortenedThrowableConverter.java index 9d42d047..ff2ebfba 100644 --- a/src/main/java/net/logstash/logback/stacktrace/ShortenedThrowableConverter.java +++ b/src/main/java/net/logstash/logback/stacktrace/ShortenedThrowableConverter.java @@ -16,7 +16,6 @@ package net.logstash.logback.stacktrace; import java.util.ArrayList; -import java.util.Arrays; import java.util.Deque; import java.util.List; import java.util.Map; @@ -28,6 +27,7 @@ import net.logstash.logback.CachingAbbreviator; import net.logstash.logback.NullAbbreviator; import net.logstash.logback.encoder.SeparatorParser; +import net.logstash.logback.util.StringUtils; import ch.qos.logback.access.PatternLayout; import ch.qos.logback.classic.pattern.Abbreviator; @@ -812,25 +812,19 @@ public void addExclude(String exclusionPattern) { } /** - * Set exclusion patterns as a list of comma separated patterns - * @param comaSeparatedPatterns list of comma separated patterns + * Add multiple exclusion patterns as a list of comma separated patterns + * @param commaSeparatedPatterns list of comma separated patterns */ - public void setExclusions(String comaSeparatedPatterns) { - if (comaSeparatedPatterns == null || comaSeparatedPatterns.isEmpty()) { - this.excludes = new ArrayList<>(5); - } else { - setExcludes(Arrays.asList(comaSeparatedPatterns.split("\\s*\\,\\s*"))); + public void addExclusions(String commaSeparatedPatterns) { + for (String regex: StringUtils.commaDelimitedListToStringArray(commaSeparatedPatterns)) { + addExclude(regex); } } - public void setExcludes(List exclusionPatterns) { - if (exclusionPatterns == null || exclusionPatterns.isEmpty()) { - this.excludes = new ArrayList<>(5); - } else { - this.excludes = new ArrayList<>(exclusionPatterns.size()); - for (String pattern : exclusionPatterns) { - addExclude(pattern); - } + public void setExcludes(List patterns) { + this.excludes = new ArrayList<>(patterns.size()); + for (String pattern : patterns) { + addExclude(pattern); } } @@ -852,13 +846,24 @@ public List getTruncateAfters() { .collect(Collectors.toList()); } - public void setTruncateAfters(String commaSeparatedPatterns) { - this.truncateAfterPatterns = new ArrayList<>(); - for (String regex: Arrays.asList(commaSeparatedPatterns.split("\\s*\\,\\s*"))) { + /** + * Add multiple truncate after patterns as a list of comma separated patterns. + * + * @param commaSeparatedPatterns list of comma separated patterns + */ + public void addTruncateAfters(String commaSeparatedPatterns) { + for (String regex: StringUtils.commaDelimitedListToStringArray(commaSeparatedPatterns)) { addTruncateAfter(regex); } } + public void setTruncateAfters(List patterns) { + this.truncateAfterPatterns = new ArrayList<>(patterns.size()); + for (String pattern: patterns) { + addTruncateAfter(pattern); + } + } + public void addEvaluator(EventEvaluator evaluator) { evaluators.add(Objects.requireNonNull(evaluator)); } diff --git a/src/main/java/net/logstash/logback/util/StringUtils.java b/src/main/java/net/logstash/logback/util/StringUtils.java index 477b5f43..baba6832 100644 --- a/src/main/java/net/logstash/logback/util/StringUtils.java +++ b/src/main/java/net/logstash/logback/util/StringUtils.java @@ -16,6 +16,11 @@ package net.logstash.logback.util; import static ch.qos.logback.core.CoreConstants.EMPTY_STRING; +import static ch.qos.logback.core.CoreConstants.EMPTY_STRING_ARRAY; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; /** * Operations on {@link java.lang.String} that are @@ -163,4 +168,72 @@ public static boolean isBlank(final CharSequence cs) { public static int length(final CharSequence cs) { return cs == null ? 0 : cs.length(); } + + /** + * Convert a comma delimited list into an + * array of strings. + * + * @param str the input {@code String} (potentially {@code null} or empty) + * @return an array of strings, or the empty array in case of empty input + * @see #delimitedListToStringArray + */ + public static String[] commaDelimitedListToStringArray(String str) { + return delimitedListToStringArray(str, ","); + } + + /** + * Take a {@code String} that is a delimited list and convert it into + * a {@code String} array. + * + *

A single {@code delimiter} may consist of more than one character, + * but it will still be considered as a single delimiter string, rather + * than as a bunch of potential delimiter characters. + * + *

Values are trimmed, and are added to the resulting array only if not blank. + * Therefore two consecutive delimiters are treated as a single delimiter. + * + *

A {@code null} delimiter is treated as no delimiter and returns an array with + * the original {@code str} string as single element. + * An empty delimiter splits the input string at each character. + * + *

A {code null} input returns an empty array. + * + * @param str the input {@code String} (potentially {@code null} or empty) + * @param delimiter the delimiter between elements + * @return an array of the tokens in the list + */ + public static String[] delimitedListToStringArray(String str, String delimiter) { + + if (str == null || str.isEmpty()) { + return EMPTY_STRING_ARRAY; + } + if (delimiter == null) { + return new String[] {str}; + } + + List result = new ArrayList<>(); + if (delimiter.isEmpty()) { + for (int i = 0; i < str.length(); i++) { + result.add(str.substring(i, i + 1)); + } + } + else { + int pos = 0; + int nextPos; + while ((nextPos = str.indexOf(delimiter, pos)) != -1) { + addIfNotBlank(result, trim(str.substring(pos, nextPos))); + pos = nextPos + delimiter.length(); + } + if (pos <= str.length()) { + addIfNotBlank(result, trim(str.substring(pos))); + } + } + return result.toArray(EMPTY_STRING_ARRAY); + } + + private static void addIfNotBlank(Collection col, String str) { + if (!isBlank(str)) { + col.add(str); + } + } } diff --git a/src/test/java/net/logstash/logback/stacktrace/ShortenedThrowableConverterTest.java b/src/test/java/net/logstash/logback/stacktrace/ShortenedThrowableConverterTest.java index 7c90bace..9272ae26 100644 --- a/src/test/java/net/logstash/logback/stacktrace/ShortenedThrowableConverterTest.java +++ b/src/test/java/net/logstash/logback/stacktrace/ShortenedThrowableConverterTest.java @@ -459,7 +459,7 @@ public void testExclusion_atEnd() { @Test public void testExclusion_commaSeparated() { ShortenedThrowableConverter converter = new ShortenedThrowableConverter(); - converter.setExclusions(".*,foo.bar"); + converter.addExclusions(",.*, foo.bar ,, , \n"); converter.start(); assertThat(converter.getExcludes()).containsExactly(".*", "foo.bar"); diff --git a/src/test/java/net/logstash/logback/util/StringUtilsTest.java b/src/test/java/net/logstash/logback/util/StringUtilsTest.java new file mode 100644 index 00000000..97b1b690 --- /dev/null +++ b/src/test/java/net/logstash/logback/util/StringUtilsTest.java @@ -0,0 +1,135 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed 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 net.logstash.logback.util; + +import static net.logstash.logback.util.StringUtils.commaDelimitedListToStringArray; +import static net.logstash.logback.util.StringUtils.delimitedListToStringArray; +import static net.logstash.logback.util.StringUtils.isBlank; +import static net.logstash.logback.util.StringUtils.isEmpty; +import static net.logstash.logback.util.StringUtils.length; +import static net.logstash.logback.util.StringUtils.trim; +import static net.logstash.logback.util.StringUtils.trimToEmpty; +import static net.logstash.logback.util.StringUtils.trimToNull; +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +/** + * @author brenuart + * + */ +public class StringUtilsTest { + + @Test + public void isBlankTest() { + assertThat(isBlank(null)).isTrue(); + assertThat(isBlank("")).isTrue(); + assertThat(isBlank(" ")).isTrue(); + assertThat(isBlank("\t")).isTrue(); + assertThat(isBlank("\n")).isTrue(); + assertThat(isBlank("\r")).isTrue(); + assertThat(isBlank(" \t\n\r")).isTrue(); + + assertThat(isBlank(" a ")).isFalse(); + } + + + @Test + public void isEmptyTest() { + assertThat(isEmpty(null)).isTrue(); + assertThat(isEmpty("")).isTrue(); + assertThat(isEmpty(" ")).isFalse(); + assertThat(isEmpty("\t")).isFalse(); + assertThat(isEmpty("\n")).isFalse(); + + assertThat(isEmpty(" a ")).isFalse(); + } + + + @Test + public void trimTest() { + assertThat(trim(null)).isNull(); + assertThat(trim("")).isEqualTo(""); + assertThat(trim(" ")).isEqualTo(""); + assertThat(trim(" foo ")).isEqualTo("foo"); + } + + + @Test + public void trimToEmptyTest() { + assertThat(trimToEmpty(null)).isEqualTo(""); + assertThat(trimToEmpty("")).isEqualTo(""); + assertThat(trimToEmpty(" ")).isEqualTo(""); + assertThat(trimToEmpty(" foo ")).isEqualTo("foo"); + } + + + @Test + public void trimToNullTest() { + assertThat(trimToNull(null)).isNull(); + assertThat(trimToNull("")).isNull(); + assertThat(trimToNull(" ")).isNull(); + assertThat(trimToNull(" foo ")).isEqualTo("foo"); + } + + + @Test + public void lengthTest() { + assertThat(length(null)).isZero(); + assertThat(length("")).isZero(); + assertThat(length(" ")).isOne(); + } + + + @Test + public void commaDelimitedListToStringArrayTest() { + // Null input -> empty array + assertThat(commaDelimitedListToStringArray(null)).isEqualTo(new String[] {}); + + // Empty string -> empty array + assertThat(commaDelimitedListToStringArray("")).isEqualTo(new String[] {}); + + // Blank string -> empty array + assertThat(commaDelimitedListToStringArray(" ")).isEqualTo(new String[] {}); + + // Trim individual values + assertThat(commaDelimitedListToStringArray("a,b,c")).isEqualTo(new String[] {"a", "b", "c"}); + assertThat(commaDelimitedListToStringArray("a, b , c \n")).isEqualTo(new String[] {"a", "b", "c"}); + + // Consecutive delimiters + assertThat(commaDelimitedListToStringArray("a,,b")).isEqualTo(new String[] {"a", "b"}); + + // Delimiter at end + assertThat(commaDelimitedListToStringArray("a,,")).isEqualTo(new String[] {"a"}); + } + + + @Test + public void delimitedListToStringArrayTest() { + // Single char delimiter + assertThat(delimitedListToStringArray("a|b", "|")).isEqualTo(new String[] {"a", "b"}); + + // Two char delimiter + assertThat(delimitedListToStringArray("a|b", "||")).isEqualTo(new String[] {"a|b"}); + assertThat(delimitedListToStringArray("a||b", "||")).isEqualTo(new String[] {"a", "b"}); + + // Empty delimiter + assertThat(delimitedListToStringArray("a,b", "")).isEqualTo(new String[] {"a", ",", "b"}); + + // Null delimiter + assertThat(delimitedListToStringArray("a,b", null)).isEqualTo(new String[] {"a,b"}); + } +}