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 24, 2024
1 parent 3cf585a commit 7473be1
Show file tree
Hide file tree
Showing 5 changed files with 229 additions and 115 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package ch.qos.logback.classic.encoder;

import ch.qos.logback.classic.spi.IThrowableProxy;
import ch.qos.logback.classic.spi.StackTraceElementProxy;

import static ch.qos.logback.classic.encoder.JsonEncoder.CAUSE_ATTR_NAME;
import static ch.qos.logback.classic.encoder.JsonEncoder.CLASS_NAME_ATTR_NAME;
import static ch.qos.logback.classic.encoder.JsonEncoder.CLOSE_ARRAY;
import static ch.qos.logback.classic.encoder.JsonEncoder.CLOSE_OBJ;
import static ch.qos.logback.classic.encoder.JsonEncoder.METHOD_NAME_ATTR_NAME;
import static ch.qos.logback.classic.encoder.JsonEncoder.OPEN_ARRAY;
import static ch.qos.logback.classic.encoder.JsonEncoder.OPEN_OBJ;
import static ch.qos.logback.classic.encoder.JsonEncoder.QUOTE;
import static ch.qos.logback.classic.encoder.JsonEncoder.QUOTE_COL;
import static ch.qos.logback.classic.encoder.JsonEncoder.STEP_ARRAY_NAME_ATTRIBUTE;
import static ch.qos.logback.classic.encoder.JsonEncoder.SUPPRESSED_ATTR_NAME;
import static ch.qos.logback.classic.encoder.JsonEncoder.VALUE_SEPARATOR;
import static ch.qos.logback.classic.encoder.JsonEncoder.appenderMember;
import static ch.qos.logback.classic.encoder.JsonEncoder.appenderMemberWithIntValue;
import static ch.qos.logback.classic.encoder.JsonEncoder.nullSafeStr;

class ArrayThrowableEncoder extends ThrowableEncoder {

private static final String FILE_NAME_ATTR_NAME = "fileName";
private static final String LINE_NUMBER_ATTR_NAME = "lineNumber";

@Override
void encodeStackTrace(StringBuilder sb, IThrowableProxy itp) {
appendSTEPArray(sb, itp.getStackTraceElementProxyArray(), itp.getCommonFrames());

IThrowableProxy cause = itp.getCause();
if (cause != null) {
sb.append(VALUE_SEPARATOR);
appendThrowableProxy(sb, CAUSE_ATTR_NAME, cause);
}

IThrowableProxy[] suppressedArray = itp.getSuppressed();
if (suppressedArray != null && suppressedArray.length != 0) {
sb.append(VALUE_SEPARATOR);
sb.append(QUOTE).append(SUPPRESSED_ATTR_NAME).append(QUOTE_COL);
sb.append(OPEN_ARRAY);
boolean first = true;
for (IThrowableProxy suppressedITP : suppressedArray) {
if (first) {
first = false;
} else {
sb.append(VALUE_SEPARATOR);
}
appendThrowableProxy(sb, null, suppressedITP);
}
sb.append(CLOSE_ARRAY);
}
}

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

int len = stepArray != null ? stepArray.length : 0;

if (commonFrames >= len) {
commonFrames = 0;
}

for (int i = 0; i < len - commonFrames; i++) {
if (i != 0)
sb.append(VALUE_SEPARATOR);

StackTraceElementProxy step = stepArray[i];

sb.append(OPEN_OBJ);
StackTraceElement ste = step.getStackTraceElement();

appenderMember(sb, CLASS_NAME_ATTR_NAME, nullSafeStr(ste.getClassName()));
sb.append(VALUE_SEPARATOR);

appenderMember(sb, METHOD_NAME_ATTR_NAME, nullSafeStr(ste.getMethodName()));
sb.append(VALUE_SEPARATOR);

appenderMember(sb, FILE_NAME_ATTR_NAME, nullSafeStr(ste.getFileName()));
sb.append(VALUE_SEPARATOR);

appenderMemberWithIntValue(sb, LINE_NUMBER_ATTR_NAME, ste.getLineNumber());
sb.append(CLOSE_OBJ);

}

sb.append(CLOSE_ARRAY);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@
package ch.qos.logback.classic.encoder;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.IThrowableProxy;
import ch.qos.logback.classic.spi.LoggerContextVO;
import ch.qos.logback.classic.spi.StackTraceElementProxy;
import ch.qos.logback.core.CoreConstants;
import ch.qos.logback.core.encoder.EncoderBase;
import org.slf4j.Marker;
Expand Down Expand Up @@ -73,8 +71,6 @@ public class JsonEncoder extends EncoderBase<ILoggingEvent> {

public static final String THROWABLE_ATTR_NAME = "throwable";

private static final String CYCLIC_THROWABLE_ATTR_NAME = "cyclic";

public static final String CAUSE_ATTR_NAME = "cause";

public static final String SUPPRESSED_ATTR_NAME = "suppressed";
Expand All @@ -83,25 +79,23 @@ public class JsonEncoder extends EncoderBase<ILoggingEvent> {

public static final String CLASS_NAME_ATTR_NAME = "className";
public static final String METHOD_NAME_ATTR_NAME = "methodName";
private static final String FILE_NAME_ATTR_NAME = "fileName";
private static final String LINE_NUMBER_ATTR_NAME = "lineNumber";

public static final String STEP_ARRAY_NAME_ATTRIBUTE = "stepArray";

private static final char OPEN_OBJ = '{';
private static final char CLOSE_OBJ = '}';
private static final char OPEN_ARRAY = '[';
private static final char CLOSE_ARRAY = ']';
static final char OPEN_OBJ = '{';
static final char CLOSE_OBJ = '}';
static final char OPEN_ARRAY = '[';
static final char CLOSE_ARRAY = ']';

private static final char QUOTE = DOUBLE_QUOTE_CHAR;
static final char QUOTE = DOUBLE_QUOTE_CHAR;
private static final char SP = ' ';
private static final char ENTRY_SEPARATOR = COLON_CHAR;

private static final String COL_SP = ": ";

private static final String QUOTE_COL = "\":";
static final String QUOTE_COL = "\":";

private static final char VALUE_SEPARATOR = COMMA_CHAR;
static final char VALUE_SEPARATOR = COMMA_CHAR;

private boolean withSequenceNumber = true;

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


Expand Down Expand Up @@ -191,7 +186,7 @@ public byte[] encode(ILoggingEvent event) {
appendArgumentArray(sb, event);

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

sb.append(CLOSE_OBJ);
sb.append(CoreConstants.JSON_LINE_SEPARATOR);
Expand Down Expand Up @@ -239,105 +234,11 @@ private void appendMap(StringBuilder sb, String attrName, Map<String, String> ma
sb.append(CLOSE_OBJ);
}

private void appendThrowableProxy(StringBuilder sb, String attributeName, IThrowableProxy itp) {

// in the nominal case, attributeName != null. However, attributeName will be null for suppressed
// IThrowableProxy array, in which case no attribute name is needed
if (attributeName != null) {
sb.append(QUOTE).append(attributeName).append(QUOTE_COL);
if (itp == null) {
sb.append(NULL_STR);
return;
}
}

sb.append(OPEN_OBJ);

appenderMember(sb, CLASS_NAME_ATTR_NAME, nullSafeStr(itp.getClassName()));

sb.append(VALUE_SEPARATOR);
appenderMember(sb, MESSAGE_ATTR_NAME, jsonEscape(itp.getMessage()));

if (itp.isCyclic()) {
sb.append(VALUE_SEPARATOR);
appenderMember(sb, CYCLIC_THROWABLE_ATTR_NAME, jsonEscape("true"));
}

sb.append(VALUE_SEPARATOR);
appendSTEPArray(sb, itp.getStackTraceElementProxyArray(), itp.getCommonFrames());

if (itp.getCommonFrames() != 0) {
sb.append(VALUE_SEPARATOR);
appenderMemberWithIntValue(sb, COMMON_FRAMES_COUNT_ATTR_NAME, itp.getCommonFrames());
}

IThrowableProxy cause = itp.getCause();
if (cause != null) {
sb.append(VALUE_SEPARATOR);
appendThrowableProxy(sb, CAUSE_ATTR_NAME, cause);
}

IThrowableProxy[] suppressedArray = itp.getSuppressed();
if (suppressedArray != null && suppressedArray.length != 0) {
sb.append(VALUE_SEPARATOR);
sb.append(QUOTE).append(SUPPRESSED_ATTR_NAME).append(QUOTE_COL);
sb.append(OPEN_ARRAY);
boolean first = true;
for (IThrowableProxy suppressedITP : suppressedArray) {
if (first) {
first = false;
} else {
sb.append(VALUE_SEPARATOR);
}
appendThrowableProxy(sb, null, suppressedITP);
}
sb.append(CLOSE_ARRAY);
}

sb.append(CLOSE_OBJ);

}

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

int len = stepArray != null ? stepArray.length : 0;

if (commonFrames >= len) {
commonFrames = 0;
}

for (int i = 0; i < len - commonFrames; i++) {
if (i != 0)
sb.append(VALUE_SEPARATOR);

StackTraceElementProxy step = stepArray[i];

sb.append(OPEN_OBJ);
StackTraceElement ste = step.getStackTraceElement();

appenderMember(sb, CLASS_NAME_ATTR_NAME, nullSafeStr(ste.getClassName()));
sb.append(VALUE_SEPARATOR);

appenderMember(sb, METHOD_NAME_ATTR_NAME, nullSafeStr(ste.getMethodName()));
sb.append(VALUE_SEPARATOR);

appenderMember(sb, FILE_NAME_ATTR_NAME, nullSafeStr(ste.getFileName()));
sb.append(VALUE_SEPARATOR);

appenderMemberWithIntValue(sb, LINE_NUMBER_ATTR_NAME, ste.getLineNumber());
sb.append(CLOSE_OBJ);

}

sb.append(CLOSE_ARRAY);
}

private void appenderMember(StringBuilder sb, String key, String value) {
static void appenderMember(StringBuilder sb, String key, String value) {
sb.append(QUOTE).append(key).append(QUOTE_COL).append(QUOTE).append(value).append(QUOTE);
}

private void appenderMemberWithIntValue(StringBuilder sb, String key, int value) {
static void appenderMemberWithIntValue(StringBuilder sb, String key, int value) {
sb.append(QUOTE).append(key).append(QUOTE_COL).append(value);
}

Expand Down Expand Up @@ -405,13 +306,13 @@ private String jsonEscapedToString(Object o) {
return jsonEscapeString(o.toString());
}

private String nullSafeStr(String s) {
static String nullSafeStr(String s) {
if (s == null)
return NULL_STR;
return s;
}

private String jsonEscape(String s) {
static String jsonEscape(String s) {
if (s == null)
return NULL_STR;
return jsonEscapeString(s);
Expand Down Expand Up @@ -512,8 +413,15 @@ public void setWithThrowable(boolean withThrowable) {
this.withThrowable = withThrowable;
}

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

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

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package ch.qos.logback.classic.encoder;

import ch.qos.logback.classic.spi.IThrowableProxy;
import ch.qos.logback.classic.spi.StackTraceElementProxy;

import static ch.qos.logback.classic.encoder.JsonEncoder.appenderMember;

class PlainStackTraceThrowableEncoder extends ThrowableEncoder {
public static final String STACKTRACE_NAME_ATTRIBUTE = "stackTrace";

@Override
void encodeStackTrace(StringBuilder sb, IThrowableProxy itp) {
appenderMember(sb, STACKTRACE_NAME_ATTRIBUTE, getOriginalStackTrace(itp));
}

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

private static void getOriginalStackTrace(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(TAB).append(step.getSTEAsString()).append(NEWLINE);
}
getOriginalStackTrace(throwable.getCause(), sb, depth + 1);
}
}
Loading

0 comments on commit 7473be1

Please sign in to comment.