Skip to content

Commit 5f8c93d

Browse files
committed
Fix off-by-one in StackTraceStringResolver (#3194)
1 parent 8218423 commit 5f8c93d

File tree

3 files changed

+74
-2
lines changed

3 files changed

+74
-2
lines changed

log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/StackTraceStringResolverTest.java

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717
package org.apache.logging.log4j.layout.template.json.resolver;
1818

19+
import static java.util.Collections.singletonList;
1920
import static org.apache.logging.log4j.layout.template.json.TestHelpers.CONFIGURATION;
2021
import static org.apache.logging.log4j.layout.template.json.TestHelpers.JAVA_BASE_PREFIX;
2122
import static org.apache.logging.log4j.layout.template.json.TestHelpers.asMap;
@@ -40,6 +41,7 @@
4041
import org.apache.logging.log4j.core.impl.Log4jLogEvent;
4142
import org.apache.logging.log4j.layout.template.json.JsonTemplateLayout;
4243
import org.apache.logging.log4j.layout.template.json.JsonTemplateLayoutDefaults;
44+
import org.apache.logging.log4j.layout.template.json.util.TruncatingBufferedPrintWriter;
4345
import org.apache.logging.log4j.util.Constants;
4446
import org.assertj.core.api.AbstractStringAssert;
4547
import org.junit.jupiter.api.Nested;
@@ -593,6 +595,67 @@ private String pointMatcherRegex(final Throwable exception) {
593595
private String matchingRegex(final String string) {
594596
return "[" + string.charAt(0) + "]" + Pattern.quote(string.substring(1));
595597
}
598+
599+
@Test
600+
void should_not_fail_on_truncated_output_not_ending_with_newline() {
601+
602+
// Try to find an exception whose truncated stack trace does not end with a newline
603+
final int maxStringLength = 100;
604+
final float maxByteCountPerChar =
605+
JsonTemplateLayoutDefaults.getCharset().newEncoder().maxBytesPerChar();
606+
final int maxStringByteCount =
607+
Math.toIntExact(Math.round(Math.ceil(maxByteCountPerChar * maxStringLength)));
608+
final TruncatingBufferedPrintWriter writer = TruncatingBufferedPrintWriter.ofCapacity(maxStringByteCount);
609+
Exception exception;
610+
String message = "m";
611+
do {
612+
exception = new Exception(message);
613+
exception.printStackTrace(writer);
614+
if (writer.truncated() && writer.buffer()[writer.length() - 1] != '\n') {
615+
break;
616+
}
617+
writer.close();
618+
message += "m";
619+
} while (true);
620+
621+
// Create the event template
622+
final String eventTemplate = writeJson(asMap(
623+
"ex",
624+
asMap(
625+
"$resolver",
626+
"exception",
627+
"field",
628+
"stackTrace",
629+
"stackTrace",
630+
asMap(
631+
"stringified",
632+
asMap(
633+
"truncation",
634+
asMap(
635+
"suffix",
636+
TRUNCATION_SUFFIX,
637+
"pointMatcherStrings",
638+
singletonList("this string shouldn't match with anything")))))));
639+
640+
// Create the layout
641+
final JsonTemplateLayout layout = JsonTemplateLayout.newBuilder()
642+
.setConfiguration(CONFIGURATION)
643+
.setEventTemplate(eventTemplate)
644+
.setMaxStringLength(maxStringLength)
645+
.setStackTraceEnabled(true)
646+
.build();
647+
648+
// Create the log event
649+
final LogEvent logEvent =
650+
Log4jLogEvent.newBuilder().setThrown(exception).build();
651+
652+
// Check the serialized event
653+
usingSerializedLogEventAccessor(layout, logEvent, accessor -> {
654+
final int expectedStackTraceLength = maxStringLength + TRUNCATION_SUFFIX.length();
655+
final String stackTrace = accessor.getString("ex");
656+
assertThat(stackTrace).hasSizeLessThan(expectedStackTraceLength);
657+
});
658+
}
596659
}
597660

598661
@Test

log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/StackTraceStringResolver.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,13 +253,14 @@ else if (bufferIndex < (endIndex - 12)
253253

254254
private static int findLineStartIndex(final CharSequence buffer, final int startIndex, final int endIndex) {
255255
char prevChar = '-';
256-
for (int i = startIndex; i <= endIndex; i++) {
256+
int i = startIndex;
257+
for (; i < endIndex; i++) {
257258
if (prevChar == '\n') {
258259
return i;
259260
}
260261
prevChar = buffer.charAt(i);
261262
}
262-
return -1;
263+
return prevChar == '\n' ? i : -1;
263264
}
264265

265266
private static int findMatchingIndex(
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<entry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xmlns="https://logging.apache.org/xml/ns"
4+
xsi:schemaLocation="https://logging.apache.org/xml/ns https://logging.apache.org/xml/ns/log4j-changelog-0.xsd"
5+
type="fixed">
6+
<issue id="3211" link="https://github.com/apache/logging-log4j2/pull/3211"/>
7+
<description format="asciidoc">Fix `ArrayIndexOutOfBoundsException` in JSON Template Layout truncated exception resolver</description>
8+
</entry>

0 commit comments

Comments
 (0)