Skip to content

Commit 0a1aeaf

Browse files
committed
Introduce StringUtils.truncate()
StringUtils.truncate() serves as central, consistent way for truncating strings used in log messages and exception failure messages, for immediate use in LogFormatUtils and ObjectUtils. See gh-30286 Closes gh-30290
1 parent 0e69bac commit 0a1aeaf

File tree

3 files changed

+68
-4
lines changed

3 files changed

+68
-4
lines changed

spring-core/src/main/java/org/springframework/core/log/LogFormatUtils.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@
2323

2424
import org.springframework.lang.Nullable;
2525
import org.springframework.util.ObjectUtils;
26+
import org.springframework.util.StringUtils;
2627

2728
/**
2829
* Utility methods for formatting and logging messages.
@@ -78,7 +79,7 @@ public static String formatValue(
7879
result = ObjectUtils.nullSafeToString(ex);
7980
}
8081
if (maxLength != -1) {
81-
result = (result.length() > maxLength ? result.substring(0, maxLength) + " (truncated)..." : result);
82+
result = StringUtils.truncate(result, maxLength);
8283
}
8384
if (replaceNewlinesAndControlCharacters) {
8485
result = NEWLINE_PATTERN.matcher(result).replaceAll("<EOL>");

spring-core/src/main/java/org/springframework/util/StringUtils.java

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -75,6 +75,10 @@ public abstract class StringUtils {
7575

7676
private static final char EXTENSION_SEPARATOR = '.';
7777

78+
private static final int DEFAULT_TRUNCATION_THRESHOLD = 100;
79+
80+
private static final String TRUNCATION_SUFFIX = " (truncated)...";
81+
7882

7983
//---------------------------------------------------------------------
8084
// General convenience methods for working with Strings
@@ -1388,4 +1392,40 @@ public static String arrayToCommaDelimitedString(@Nullable Object[] arr) {
13881392
return arrayToDelimitedString(arr, ",");
13891393
}
13901394

1395+
/**
1396+
* Truncate the supplied {@link CharSequence}.
1397+
* <p>Delegates to {@link #truncate(CharSequence, int)}, supplying {@code 100}
1398+
* as the threshold.
1399+
* @param charSequence the {@code CharSequence} to truncate
1400+
* @return a truncated string, or a string representation of the original
1401+
* {@code CharSequence} if its length does not exceed the threshold
1402+
* @since 5.3.27
1403+
*/
1404+
public static String truncate(CharSequence charSequence) {
1405+
return truncate(charSequence, DEFAULT_TRUNCATION_THRESHOLD);
1406+
}
1407+
1408+
/**
1409+
* Truncate the supplied {@link CharSequence}.
1410+
* <p>If the length of the {@code CharSequence} is greater than the threshold,
1411+
* this method returns a {@linkplain CharSequence#subSequence(int, int)
1412+
* subsequence} of the {@code CharSequence} (up to the threshold) appended
1413+
* with the suffix {@code " (truncated)..."}. Otherwise, this method returns
1414+
* {@code charSequence.toString()}.
1415+
* @param charSequence the {@code CharSequence} to truncate
1416+
* @param threshold the maximum length after which to truncate; must be a
1417+
* positive number
1418+
* @return a truncated string, or a string representation of the original
1419+
* {@code CharSequence} if its length does not exceed the threshold
1420+
* @since 5.3.27
1421+
*/
1422+
public static String truncate(CharSequence charSequence, int threshold) {
1423+
Assert.isTrue(threshold > 0,
1424+
() -> "Truncation threshold must be a positive number: " + threshold);
1425+
if (charSequence.length() > threshold) {
1426+
return charSequence.subSequence(0, threshold) + TRUNCATION_SUFFIX;
1427+
}
1428+
return charSequence.toString();
1429+
}
1430+
13911431
}

spring-core/src/test/java/org/springframework/util/StringUtilsTests.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,6 +22,8 @@
2222
import java.util.Properties;
2323

2424
import org.junit.jupiter.api.Test;
25+
import org.junit.jupiter.params.ParameterizedTest;
26+
import org.junit.jupiter.params.provider.CsvSource;
2527

2628
import static org.assertj.core.api.Assertions.assertThat;
2729
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
@@ -785,4 +787,25 @@ void collectionToDelimitedStringWithNullValuesShouldNotFail() {
785787
assertThat(StringUtils.collectionToCommaDelimitedString(Collections.singletonList(null))).isEqualTo("null");
786788
}
787789

790+
@Test
791+
void truncatePreconditions() {
792+
assertThatIllegalArgumentException()
793+
.isThrownBy(() -> StringUtils.truncate("foo", 0))
794+
.withMessage("Truncation threshold must be a positive number: 0");
795+
assertThatIllegalArgumentException()
796+
.isThrownBy(() -> StringUtils.truncate("foo", -99))
797+
.withMessage("Truncation threshold must be a positive number: -99");
798+
}
799+
800+
@ParameterizedTest
801+
@CsvSource(delimiterString = "-->", value = {
802+
"aardvark --> aardvark",
803+
"aardvark12 --> aardvark12",
804+
"aardvark123 --> aardvark12 (truncated)...",
805+
"aardvark, bird, cat --> aardvark, (truncated)..."
806+
})
807+
void truncate(String text, String truncated) {
808+
assertThat(StringUtils.truncate(text, 10)).isEqualTo(truncated);
809+
}
810+
788811
}

0 commit comments

Comments
 (0)