diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/encoder/JsonEncoder.java b/logback-classic/src/main/java/ch/qos/logback/classic/encoder/JsonEncoder.java index 36864c6104..18c61461c5 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/encoder/JsonEncoder.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/encoder/JsonEncoder.java @@ -23,6 +23,7 @@ import org.slf4j.Marker; import org.slf4j.event.KeyValuePair; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; @@ -108,7 +109,6 @@ public class JsonEncoder extends EncoderBase { private boolean withTimestamp = true; private boolean withNanoseconds = true; - private boolean withLevel = true; private boolean withThreadName = true; private boolean withLoggerName = true; @@ -121,7 +121,6 @@ public class JsonEncoder extends EncoderBase { private boolean withThrowable = true; private boolean withFormattedMessage = false; - @Override public byte[] headerBytes() { return EMPTY_BYTES; @@ -135,39 +134,40 @@ public byte[] encode(ILoggingEvent event) { if (withSequenceNumber) { appenderMemberWithLongValue(sb, SEQUENCE_NUMBER_ATTR_NAME, event.getSequenceNumber()); - sb.append(VALUE_SEPARATOR); } if (withTimestamp) { + appendValueSeparator(sb, withSequenceNumber); appenderMemberWithLongValue(sb, TIMESTAMP_ATTR_NAME, event.getTimeStamp()); - sb.append(VALUE_SEPARATOR); } if (withNanoseconds) { + appendValueSeparator(sb, withSequenceNumber, withTimestamp); appenderMemberWithLongValue(sb, NANOSECONDS_ATTR_NAME, event.getNanoseconds()); - sb.append(VALUE_SEPARATOR); } if (withLevel) { + appendValueSeparator(sb, withNanoseconds, withSequenceNumber, withTimestamp); String levelStr = event.getLevel() != null ? event.getLevel().levelStr : NULL_STR; appenderMember(sb, LEVEL_ATTR_NAME, levelStr); - sb.append(VALUE_SEPARATOR); } if (withThreadName) { + appendValueSeparator(sb, withLevel, withNanoseconds, withSequenceNumber, withTimestamp); appenderMember(sb, THREAD_NAME_ATTR_NAME, jsonEscape(event.getThreadName())); - sb.append(VALUE_SEPARATOR); } if (withLoggerName) { + appendValueSeparator(sb, withThreadName, withLevel, withNanoseconds, withSequenceNumber, withTimestamp); appenderMember(sb, LOGGER_ATTR_NAME, event.getLoggerName()); - sb.append(VALUE_SEPARATOR); } if (withContext) { - appendLoggerContext(sb, event.getLoggerContextVO()); + // at this stage we assume that at least one field was written sb.append(VALUE_SEPARATOR); + appendLoggerContext(sb, event.getLoggerContextVO()); } + if (withMarkers) appendMarkers(sb, event); @@ -178,17 +178,18 @@ public byte[] encode(ILoggingEvent event) { appendKeyValuePairs(sb, event); if (withMessage) { - appenderMember(sb, MESSAGE_ATTR_NAME, jsonEscape(event.getMessage())); sb.append(VALUE_SEPARATOR); + appenderMember(sb, MESSAGE_ATTR_NAME, jsonEscape(event.getMessage())); } if (withFormattedMessage) { - appenderMember(sb, FORMATTED_MESSAGE_ATTR_NAME, jsonEscape(event.getFormattedMessage())); sb.append(VALUE_SEPARATOR); + appenderMember(sb, FORMATTED_MESSAGE_ATTR_NAME, jsonEscape(event.getFormattedMessage())); } - if (withArguments) + if (withArguments) { appendArgumentArray(sb, event); + } if (withThrowable) appendThrowableProxy(sb, THROWABLE_ATTR_NAME, event.getThrowableProxy()); @@ -198,6 +199,19 @@ public byte[] encode(ILoggingEvent event) { return sb.toString().getBytes(UTF_8_CHARSET); } + void appendValueSeparator(StringBuilder sb, boolean... subsequentConditionals) { + boolean enabled = false; + for (boolean subsequent : subsequentConditionals) { + if (subsequent) { + enabled = true; + break; + } + } + + if (enabled) + sb.append(VALUE_SEPARATOR); + } + private void appendLoggerContext(StringBuilder sb, LoggerContextVO loggerContextVO) { sb.append(QUOTE).append(CONTEXT_ATTR_NAME).append(QUOTE_COL); @@ -240,6 +254,13 @@ private void appendMap(StringBuilder sb, String attrName, Map ma } private void appendThrowableProxy(StringBuilder sb, String attributeName, IThrowableProxy itp) { + appendThrowableProxy(sb, attributeName, itp, true); + } + + private void appendThrowableProxy(StringBuilder sb, String attributeName, IThrowableProxy itp, boolean appendValueSeparator) { + + if (appendValueSeparator) + sb.append(VALUE_SEPARATOR); // in the nominal case, attributeName != null. However, attributeName will be null for suppressed // IThrowableProxy array, in which case no attribute name is needed @@ -273,7 +294,6 @@ private void appendThrowableProxy(StringBuilder sb, String attributeName, IThrow IThrowableProxy cause = itp.getCause(); if (cause != null) { - sb.append(VALUE_SEPARATOR); appendThrowableProxy(sb, CAUSE_ATTR_NAME, cause); } @@ -282,14 +302,12 @@ private void appendThrowableProxy(StringBuilder sb, String attributeName, IThrow 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) { + appendThrowableProxy(sb, null, suppressedITP, !first); + if (first) first = false; - } else { - sb.append(VALUE_SEPARATOR); - } - appendThrowableProxy(sb, null, suppressedITP); } sb.append(CLOSE_ARRAY); } @@ -350,6 +368,7 @@ private void appendKeyValuePairs(StringBuilder sb, ILoggingEvent event) { if (kvpList == null || kvpList.isEmpty()) return; + sb.append(VALUE_SEPARATOR); sb.append(QUOTE).append(KEY_VALUE_PAIRS_ATTR_NAME).append(QUOTE_COL).append(SP).append(OPEN_ARRAY); final int len = kvpList.size(); for (int i = 0; i < len; i++) { @@ -361,7 +380,6 @@ private void appendKeyValuePairs(StringBuilder sb, ILoggingEvent event) { sb.append(CLOSE_OBJ); } sb.append(CLOSE_ARRAY); - sb.append(VALUE_SEPARATOR); } private void appendArgumentArray(StringBuilder sb, ILoggingEvent event) { @@ -369,6 +387,7 @@ private void appendArgumentArray(StringBuilder sb, ILoggingEvent event) { if (argumentArray == null) return; + sb.append(VALUE_SEPARATOR); sb.append(QUOTE).append(ARGUMENT_ARRAY_ATTR_NAME).append(QUOTE_COL).append(SP).append(OPEN_ARRAY); final int len = argumentArray.length; for (int i = 0; i < len; i++) { @@ -378,7 +397,6 @@ private void appendArgumentArray(StringBuilder sb, ILoggingEvent event) { } sb.append(CLOSE_ARRAY); - sb.append(VALUE_SEPARATOR); } private void appendMarkers(StringBuilder sb, ILoggingEvent event) { @@ -386,6 +404,7 @@ private void appendMarkers(StringBuilder sb, ILoggingEvent event) { if (markerList == null) return; + sb.append(VALUE_SEPARATOR); sb.append(QUOTE).append(MARKERS_ATTR_NAME).append(QUOTE_COL).append(SP).append(OPEN_ARRAY); final int len = markerList.size(); for (int i = 0; i < len; i++) { @@ -395,8 +414,6 @@ private void appendMarkers(StringBuilder sb, ILoggingEvent event) { } sb.append(CLOSE_ARRAY); - sb.append(VALUE_SEPARATOR); - } private String jsonEscapedToString(Object o) { @@ -419,7 +436,7 @@ private String jsonEscape(String s) { private void appendMDC(StringBuilder sb, ILoggingEvent event) { Map map = event.getMDCPropertyMap(); - + sb.append(VALUE_SEPARATOR); sb.append(QUOTE).append(MDC_ATTR_NAME).append(QUOTE_COL).append(SP).append(OPEN_OBJ); if (isNotEmptyMap(map)) { Set> entrySet = map.entrySet(); @@ -433,8 +450,6 @@ private void appendMDC(StringBuilder sb, ILoggingEvent event) { } sb.append(CLOSE_OBJ); - sb.append(VALUE_SEPARATOR); - } boolean isNotEmptyMap(Map map) { diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/encoder/JsonEncoderTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/encoder/JsonEncoderTest.java index c71672f168..622260e4d0 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/encoder/JsonEncoderTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/encoder/JsonEncoderTest.java @@ -99,10 +99,8 @@ void smoke() throws JsonProcessingException { byte[] resultBytes = jsonEncoder.encode(event); String resultString = new String(resultBytes, StandardCharsets.UTF_8); //System.out.println(resultString); - JsonLoggingEvent resultEvent = stringToLoggingEventMapper.mapStringToLoggingEvent(resultString); compareEvents(event, resultEvent); - } @Test @@ -219,7 +217,7 @@ void withFormattedMessage() throws JsonProcessingException { byte[] resultBytes = jsonEncoder.encode(event); String resultString = new String(resultBytes, StandardCharsets.UTF_8); - System.out.println(resultString); + //System.out.println(resultString); JsonLoggingEvent resultEvent = stringToLoggingEventMapper.mapStringToLoggingEvent(resultString); compareEvents(event, resultEvent); @@ -257,6 +255,23 @@ void withThrowable() throws JsonProcessingException { compareEvents(event, resultEvent); } + @Test + void withThrowableDisabled() throws JsonProcessingException { + Throwable t = new RuntimeException("withThrowableDisabled"); + LoggingEvent event = new LoggingEvent("in withThrowable test", logger, Level.WARN, "hello kvp", t, null); + jsonEncoder.setWithThrowable(false); + byte[] resultBytes = jsonEncoder.encode(event); + String resultString = new String(resultBytes, StandardCharsets.UTF_8); + //System.out.println(resultString); + JsonLoggingEvent resultEvent = stringToLoggingEventMapper.mapStringToLoggingEvent(resultString); + + LoggingEvent eventWithNoThrowable = new LoggingEvent("in withThrowable test", logger, Level.WARN, "hello kvp", null, null); + eventWithNoThrowable.setTimeStamp(event.getTimeStamp()); + + compareEvents(eventWithNoThrowable, resultEvent); + } + + @Test void withThrowableHavingCause() throws JsonProcessingException { Throwable cause = new IllegalStateException("test cause");