Skip to content

Commit 9471624

Browse files
committed
add option to encode Throwable in a raw format for easy ingestion into logging backends
Signed-off-by: Gregor Zeitlinger <[email protected]>
1 parent 7812a55 commit 9471624

File tree

2 files changed

+54
-3
lines changed

2 files changed

+54
-3
lines changed

logback-classic/src/main/java/ch/qos/logback/classic/encoder/JsonEncoder.java

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ public class JsonEncoder extends EncoderBase<ILoggingEvent> {
103103

104104
private static final char VALUE_SEPARATOR = COMMA_CHAR;
105105

106+
private static final String NEWLINE = "\\\\n";
107+
106108
private boolean withSequenceNumber = true;
107109

108110
private boolean withTimestamp = true;
@@ -119,6 +121,7 @@ public class JsonEncoder extends EncoderBase<ILoggingEvent> {
119121
private boolean withMessage = true;
120122
private boolean withArguments = true;
121123
private boolean withThrowable = true;
124+
private boolean withRawThrowable = false;
122125
private boolean withFormattedMessage = false;
123126

124127

@@ -190,6 +193,11 @@ public byte[] encode(ILoggingEvent event) {
190193
if (withArguments)
191194
appendArgumentArray(sb, event);
192195

196+
if (withRawThrowable) {
197+
appenderMember(sb, "rawThrowable", event.getThrowableProxy() != null ? getRawThrowable(event.getThrowableProxy()) : NULL_STR);
198+
sb.append(VALUE_SEPARATOR);
199+
}
200+
193201
if (withThrowable)
194202
appendThrowableProxy(sb, THROWABLE_ATTR_NAME, event.getThrowableProxy());
195203

@@ -298,6 +306,26 @@ private void appendThrowableProxy(StringBuilder sb, String attributeName, IThrow
298306

299307
}
300308

309+
private static String getRawThrowable(IThrowableProxy throwableProxy) {
310+
StringBuilder sb = new StringBuilder();
311+
getRawThrowable(throwableProxy, sb, 0);
312+
return sb.toString();
313+
}
314+
315+
private static void getRawThrowable(IThrowableProxy throwable, StringBuilder sb, int depth) {
316+
if (throwable == null) {
317+
return;
318+
}
319+
if (depth > 0) {
320+
sb.append("Caused by: ");
321+
}
322+
sb.append(throwable.getClassName()).append(": ").append(throwable.getMessage()).append(NEWLINE);
323+
for (StackTraceElementProxy step : throwable.getStackTraceElementProxyArray()) {
324+
sb.append(" ").append(step).append(NEWLINE);
325+
}
326+
getRawThrowable(throwable.getCause(), sb, depth + 1);
327+
}
328+
301329
private void appendSTEPArray(StringBuilder sb, StackTraceElementProxy[] stepArray, int commonFrames) {
302330
sb.append(QUOTE).append(STEP_ARRAY_NAME_ATTRIBUTE).append(QUOTE_COL).append(OPEN_ARRAY);
303331

@@ -512,8 +540,15 @@ public void setWithThrowable(boolean withThrowable) {
512540
this.withThrowable = withThrowable;
513541
}
514542

543+
/**
544+
* @param withRawThrowable
545+
* @since 1.5.7
546+
*/
547+
public void setWithRawThrowable(boolean withRawThrowable) {
548+
this.withRawThrowable = withRawThrowable;
549+
}
550+
515551
public void setWithFormattedMessage(boolean withFormattedMessage) {
516552
this.withFormattedMessage = withFormattedMessage;
517553
}
518-
519554
}

logback-classic/src/test/java/ch/qos/logback/classic/encoder/JsonEncoderTest.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
import ch.qos.logback.core.read.ListAppender;
3030
import ch.qos.logback.core.status.testUtil.StatusChecker;
3131
import ch.qos.logback.core.testUtil.RandomUtil;
32-
import ch.qos.logback.core.util.StatusPrinter;
3332
import com.fasterxml.jackson.core.JsonProcessingException;
3433
import org.junit.jupiter.api.AfterEach;
3534
import org.junit.jupiter.api.BeforeEach;
@@ -48,6 +47,7 @@
4847
import java.util.Map;
4948
import java.util.Objects;
5049

50+
import static org.assertj.core.api.Assertions.assertThat;
5151
import static org.junit.jupiter.api.Assertions.assertEquals;
5252
import static org.junit.jupiter.api.Assertions.assertTrue;
5353

@@ -257,6 +257,22 @@ void withThrowable() throws JsonProcessingException {
257257
compareEvents(event, resultEvent);
258258
}
259259

260+
@Test
261+
void withRawThrowable() throws IOException {
262+
Throwable t = new RuntimeException("test", new IllegalStateException("test cause"));
263+
LoggingEvent event = new LoggingEvent("in withThrowable test", logger, Level.WARN, "hello kvp", t, null);
264+
265+
jsonEncoder.setWithRawThrowable(true);
266+
byte[] resultBytes = jsonEncoder.encode(event);
267+
String resultString = new String(resultBytes, StandardCharsets.UTF_8).trim();
268+
269+
//testing the whole stack trace is brittle - depends on the tool used, e.g. is different in an IDE
270+
//and contains line numbers that can easily change
271+
272+
assertThat(resultString).contains("\"java.lang.RuntimeException: test\\\\n at ch.qos.logback.classic");
273+
assertThat(resultString).contains("Caused by: java.lang.IllegalStateException: test cause\\\\n at ch.qos.logback.classic");
274+
}
275+
260276
@Test
261277
void withThrowableHavingCause() throws JsonProcessingException {
262278
Throwable cause = new IllegalStateException("test cause");
@@ -371,4 +387,4 @@ void withJoranAndEnabledFormattedMessage() throws JoranException, IOException {
371387

372388
assertEquals(withness, lines.get(0));
373389
}
374-
}
390+
}

0 commit comments

Comments
 (0)