Skip to content

Commit

Permalink
add option to encode Throwable in a raw format for easy ingestion int…
Browse files Browse the repository at this point in the history
…o logging backends

Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>
  • Loading branch information
zeitlinger committed Apr 22, 2024
1 parent 7812a55 commit 9471624
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ public class JsonEncoder extends EncoderBase<ILoggingEvent> {

private static final char VALUE_SEPARATOR = COMMA_CHAR;

private static final String NEWLINE = "\\\\n";

private boolean withSequenceNumber = true;

private boolean withTimestamp = true;
Expand All @@ -119,6 +121,7 @@ public class JsonEncoder extends EncoderBase<ILoggingEvent> {
private boolean withMessage = true;
private boolean withArguments = true;
private boolean withThrowable = true;
private boolean withRawThrowable = false;
private boolean withFormattedMessage = false;


Expand Down Expand Up @@ -190,6 +193,11 @@ public byte[] encode(ILoggingEvent event) {
if (withArguments)
appendArgumentArray(sb, event);

if (withRawThrowable) {
appenderMember(sb, "rawThrowable", event.getThrowableProxy() != null ? getRawThrowable(event.getThrowableProxy()) : NULL_STR);
sb.append(VALUE_SEPARATOR);
}

if (withThrowable)
appendThrowableProxy(sb, THROWABLE_ATTR_NAME, event.getThrowableProxy());

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

}

private static String getRawThrowable(IThrowableProxy throwableProxy) {
StringBuilder sb = new StringBuilder();
getRawThrowable(throwableProxy, sb, 0);
return sb.toString();
}

private static void getRawThrowable(IThrowableProxy throwable, StringBuilder sb, int depth) {
if (throwable == null) {
return;
}
if (depth > 0) {
sb.append("Caused by: ");
}
sb.append(throwable.getClassName()).append(": ").append(throwable.getMessage()).append(NEWLINE);
for (StackTraceElementProxy step : throwable.getStackTraceElementProxyArray()) {
sb.append(" ").append(step).append(NEWLINE);
}
getRawThrowable(throwable.getCause(), sb, depth + 1);
}

private void appendSTEPArray(StringBuilder sb, StackTraceElementProxy[] stepArray, int commonFrames) {
sb.append(QUOTE).append(STEP_ARRAY_NAME_ATTRIBUTE).append(QUOTE_COL).append(OPEN_ARRAY);

Expand Down Expand Up @@ -512,8 +540,15 @@ public void setWithThrowable(boolean withThrowable) {
this.withThrowable = withThrowable;
}

/**
* @param withRawThrowable
* @since 1.5.7
*/
public void setWithRawThrowable(boolean withRawThrowable) {
this.withRawThrowable = withRawThrowable;
}

public void setWithFormattedMessage(boolean withFormattedMessage) {
this.withFormattedMessage = withFormattedMessage;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
import ch.qos.logback.core.read.ListAppender;
import ch.qos.logback.core.status.testUtil.StatusChecker;
import ch.qos.logback.core.testUtil.RandomUtil;
import ch.qos.logback.core.util.StatusPrinter;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
Expand All @@ -48,6 +47,7 @@
import java.util.Map;
import java.util.Objects;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

Expand Down Expand Up @@ -257,6 +257,22 @@ void withThrowable() throws JsonProcessingException {
compareEvents(event, resultEvent);
}

@Test
void withRawThrowable() throws IOException {
Throwable t = new RuntimeException("test", new IllegalStateException("test cause"));
LoggingEvent event = new LoggingEvent("in withThrowable test", logger, Level.WARN, "hello kvp", t, null);

jsonEncoder.setWithRawThrowable(true);
byte[] resultBytes = jsonEncoder.encode(event);
String resultString = new String(resultBytes, StandardCharsets.UTF_8).trim();

//testing the whole stack trace is brittle - depends on the tool used, e.g. is different in an IDE
//and contains line numbers that can easily change

assertThat(resultString).contains("\"java.lang.RuntimeException: test\\\\n at ch.qos.logback.classic");
assertThat(resultString).contains("Caused by: java.lang.IllegalStateException: test cause\\\\n at ch.qos.logback.classic");
}

@Test
void withThrowableHavingCause() throws JsonProcessingException {
Throwable cause = new IllegalStateException("test cause");
Expand Down Expand Up @@ -371,4 +387,4 @@ void withJoranAndEnabledFormattedMessage() throws JoranException, IOException {

assertEquals(withness, lines.get(0));
}
}
}

0 comments on commit 9471624

Please sign in to comment.