From 9e833ec858953a2296afdc3292f8542fc08f2a45 Mon Sep 17 00:00:00 2001 From: Ceki Gulcu Date: Thu, 11 May 2023 18:51:40 +0200 Subject: [PATCH] ongoing work on JsonEncoder LOGBACK-1734 Signed-off-by: Ceki Gulcu --- logback-classic/pom.xml | 9 + .../logback/classic/encoder/JsonEncoder.java | 260 +++++++++++++----- .../classic/spi/ClassPackagingData.java | 23 +- .../logback/classic/spi/LoggerContextVO.java | 6 +- .../classic/spi/StackTraceElementProxy.java | 16 +- logback-classic/src/test/input/fqcn.txt | 2 +- .../ch/qos/logback/classic/corpus/Corpus.java | 3 +- .../logback/classic/corpus/CorpusModel.java | 5 +- .../classic/encoder/JsonEncoderTest.java | 245 +++++++++++++++++ .../logback/classic/html/HTMLLayoutTest.java | 4 +- .../classic/jsonTest/JsonLoggingEvent.java | 198 +++++++++++++ .../JsonStringToLoggingEventMapper.java | 104 +++++++ .../jsonTest/KeyValuePairDeserializer.java | 58 ++++ .../classic/jsonTest/LevelDeserializer.java | 44 +++ .../jsonTest/LoggerContextVODeserializer.java | 65 +++++ .../classic/jsonTest/MarkerDeserializer.java | 50 ++++ .../classic/jsonTest/STEPDeserializer.java | 50 ++++ .../jsonTest/ThrowableProxyComparator.java | 110 ++++++++ .../classic/spi/PubLoggerContextVO.java | 66 +++++ .../classic/spi/PubLoggingEventVO.java | 9 +- ...wableProxy.java => PubThrowableProxy.java} | 12 +- pom.xml | 1 + 22 files changed, 1254 insertions(+), 86 deletions(-) create mode 100644 logback-classic/src/test/java/ch/qos/logback/classic/encoder/JsonEncoderTest.java create mode 100644 logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/JsonLoggingEvent.java create mode 100644 logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/JsonStringToLoggingEventMapper.java create mode 100644 logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/KeyValuePairDeserializer.java create mode 100644 logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/LevelDeserializer.java create mode 100644 logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/LoggerContextVODeserializer.java create mode 100644 logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/MarkerDeserializer.java create mode 100644 logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/STEPDeserializer.java create mode 100644 logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/ThrowableProxyComparator.java create mode 100644 logback-classic/src/test/java/ch/qos/logback/classic/spi/PubLoggerContextVO.java rename logback-classic/src/test/java/ch/qos/logback/classic/spi/{DummyThrowableProxy.java => PubThrowableProxy.java} (86%) diff --git a/logback-classic/pom.xml b/logback-classic/pom.xml index 1baa734fc4..b3ae4d9c2d 100755 --- a/logback-classic/pom.xml +++ b/logback-classic/pom.xml @@ -130,6 +130,13 @@ test + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + test + + @@ -263,9 +270,11 @@ --add-modules jakarta.servlet --add-opens ch.qos.logback.core/ch.qos.logback.core.testUtil=java.naming --add-opens ch.qos.logback.classic/ch.qos.logback.classic.testUtil=ch.qos.logback.core + --add-opens ch.qos.logback.classic/ch.qos.logback.classic.jsonTest=ALL-UNNAMED classes 8 + 1C true 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 56fba9a0e7..9be688573a 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 @@ -14,14 +14,17 @@ package ch.qos.logback.classic.encoder; -import ch.qos.logback.classic.Level; 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.encoder.EncoderBase; import org.slf4j.Marker; import org.slf4j.event.KeyValuePair; import java.util.List; import java.util.Map; +import java.util.Set; import static ch.qos.logback.core.CoreConstants.COLON_CHAR; import static ch.qos.logback.core.CoreConstants.COMMA_CHAR; @@ -31,39 +34,48 @@ import static ch.qos.logback.core.model.ModelConstants.NULL_STR; /** - * * */ public class JsonEncoder extends EncoderBase { - - - + static final boolean DO_NOT_ADD_QUOTE_KEY = false; + static final boolean ADD_QUOTE_KEY = true; static int DEFAULT_SIZE = 1024; - static int DEFAULT_SIZE_WITH_THROWABLE = DEFAULT_SIZE*8; + static int DEFAULT_SIZE_WITH_THROWABLE = DEFAULT_SIZE * 8; static byte[] EMPTY_BYTES = new byte[0]; - public static final String CONTEXT_ATTR_NAME = "context"; + public static final String NAME_ATTR_NAME = "name"; + public static final String BIRTHDATE_ATTR_NAME = "birthdate"; + public static final String CONTEXT_PROPERTIES_ATTR_NAME = "properties"; + + public static final String TIMESTAMP_ATTR_NAME = "timestamp"; public static final String NANOSECONDS_ATTR_NAME = "nanoseconds"; - public static final String SEQUENCE_NUMBER_ATTR_NAME = "sequenceNumbers"; - + public static final String SEQUENCE_NUMBER_ATTR_NAME = "sequenceNumber"; public static final String LEVEL_ATTR_NAME = "level"; public static final String MARKERS_ATTR_NAME = "markers"; - public static final String THREAD_ATTR_NAME = "thread"; + public static final String THREAD_NAME_ATTR_NAME = "threadName"; public static final String MDC_ATTR_NAME = "mdc"; - public static final String LOGGER_ATTR_NAME = "logger"; - public static final String MESSAGE_ATTR_NAME = "rawMessage"; + public static final String LOGGER_ATTR_NAME = "loggerName"; + + public static final String MESSAGE_ATTR_NAME = "message"; public static final String ARGUMENT_ARRAY_ATTR_NAME = "arguments"; - public static final String KEY_VALUE_PAIRS_ATTR_NAME = "keyValuePairs"; + public static final String KEY_VALUE_PAIRS_ATTR_NAME = "kvpList"; public static final String THROWABLE_ATTR_NAME = "throwable"; + private static final String CLASS_NAME_ATTR_NAME = "className"; + private 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"; + + private 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 = '['; @@ -73,11 +85,12 @@ public class JsonEncoder extends EncoderBase { private static final char SP = ' '; private static final char ENTRY_SEPARATOR = COLON_CHAR; - private static final char COL_SP = COLON_CHAR+SP; + private static final String COL_SP = ": "; - private static final char VALUE_SEPARATOR = COMMA_CHAR; + private static final String QUOTE_COL = "\":"; + private static final char VALUE_SEPARATOR = COMMA_CHAR; @Override public byte[] headerBytes() { @@ -86,130 +99,251 @@ public byte[] headerBytes() { @Override public byte[] encode(ILoggingEvent event) { - final int initialCapacity = event.getThrowableProxy() == null ? DEFAULT_SIZE: DEFAULT_SIZE_WITH_THROWABLE; + final int initialCapacity = event.getThrowableProxy() == null ? DEFAULT_SIZE : DEFAULT_SIZE_WITH_THROWABLE; StringBuilder sb = new StringBuilder(initialCapacity); sb.append(OPEN_OBJ); - - sb.append(SEQUENCE_NUMBER_ATTR_NAME).append(COL_SP).append(event.getSequenceNumber()); + appenderMemberWithLongValue(sb, SEQUENCE_NUMBER_ATTR_NAME, event.getSequenceNumber()); sb.append(VALUE_SEPARATOR); - - sb.append(TIMESTAMP_ATTR_NAME).append(COL_SP).append(event.getTimeStamp()); + appenderMemberWithLongValue(sb, TIMESTAMP_ATTR_NAME, event.getTimeStamp()); sb.append(VALUE_SEPARATOR); - sb.append(NANOSECONDS_ATTR_NAME).append(COL_SP).append(event.getNanoseconds()); + appenderMemberWithLongValue(sb, NANOSECONDS_ATTR_NAME, event.getNanoseconds()); sb.append(VALUE_SEPARATOR); String levelStr = event.getLevel() != null ? event.getLevel().levelStr : NULL_STR; - sb.append(LEVEL_ATTR_NAME).append(COL_SP).append(QUOTE).append(levelStr).append(QUOTE); + appenderMember(sb, LEVEL_ATTR_NAME, levelStr); + sb.append(VALUE_SEPARATOR); + + appenderMember(sb, THREAD_NAME_ATTR_NAME, jsonEscape(event.getThreadName())); sb.append(VALUE_SEPARATOR); - sb.append(THREAD_ATTR_NAME).append(COL_SP).append(QUOTE).append(jsonSafeStr(event.getThreadName())).append(QUOTE); + appenderMember(sb, LOGGER_ATTR_NAME, event.getLoggerName()); sb.append(VALUE_SEPARATOR); - sb.append(LOGGER_ATTR_NAME).append(COL_SP).append(QUOTE).append(event.getLoggerName()).append(QUOTE); + appendLoggerContext(sb, event.getLoggerContextVO()); sb.append(VALUE_SEPARATOR); appendMarkers(sb, event); + appendMDC(sb, event); + appendKeyValuePairs(sb, event); - sb.append(MESSAGE_ATTR_NAME).append(COL_SP).append(QUOTE).append(jsonSafeStr(event.getMessage())).append(QUOTE); + appenderMember(sb, MESSAGE_ATTR_NAME, jsonEscape(event.getMessage())); sb.append(VALUE_SEPARATOR); appendArgumentArray(sb, event); + appendThrowableProxy(sb, event); sb.append(CLOSE_OBJ); return sb.toString().getBytes(UTF_8_CHARSET); } + private void appendLoggerContext(StringBuilder sb, LoggerContextVO loggerContextVO) { + + + sb.append(QUOTE).append(CONTEXT_ATTR_NAME).append(QUOTE_COL); + if (loggerContextVO == null) { + sb.append(NULL_STR); + return; + } + + sb.append(OPEN_OBJ); + appenderMember(sb, NAME_ATTR_NAME, nullSafeStr(loggerContextVO.getName())); + sb.append(VALUE_SEPARATOR); + appenderMemberWithLongValue(sb, BIRTHDATE_ATTR_NAME, loggerContextVO.getBirthTime()); + sb.append(VALUE_SEPARATOR); + + appendMap(sb, CONTEXT_PROPERTIES_ATTR_NAME, loggerContextVO.getPropertyMap()); + sb.append(CLOSE_OBJ); + + } + + private void appendMap(StringBuilder sb, String attrName, Map map) { + sb.append(QUOTE).append(attrName).append(QUOTE_COL); + if(map == null) { + sb.append(NULL_STR); + return; + } + + sb.append(OPEN_OBJ); + + + boolean addComma = false; + Set> entries = map.entrySet(); + for(Map.Entry entry: entries) { + if(addComma) { + sb.append(VALUE_SEPARATOR); + } + addComma = true; + appenderMember(sb, jsonEscapedToString(entry.getKey()), jsonEscapedToString(entry.getValue())); + } + + sb.append(CLOSE_OBJ); + + + + } + + + private void appendThrowableProxy(StringBuilder sb, ILoggingEvent event) { + IThrowableProxy itp = event.getThrowableProxy(); + sb.append(QUOTE).append(THROWABLE_ATTR_NAME).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())); + sb.append(VALUE_SEPARATOR); + + StackTraceElementProxy[] stepArray = itp.getStackTraceElementProxyArray(); + + sb.append(QUOTE).append(STEP_ARRAY_NAME_ATTRIBUTE).append(QUOTE_COL).append(OPEN_ARRAY); + + int len = stepArray != null ? stepArray.length : 0; + for (int i = 0; i < len; 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); + sb.append(CLOSE_OBJ); + + } + + private 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) { + sb.append(QUOTE).append(key).append(QUOTE_COL).append(value); + } + private void appenderMemberWithLongValue(StringBuilder sb, String key, long value) { + sb.append(QUOTE).append(key).append(QUOTE_COL).append(value); + } + private void appendKeyValuePairs(StringBuilder sb, ILoggingEvent event) { List kvpList = event.getKeyValuePairs(); - if(kvpList == null || kvpList.isEmpty()) + if (kvpList == null || kvpList.isEmpty()) return; - sb.append(KEY_VALUE_PAIRS_ATTR_NAME).append(ENTRY_SEPARATOR).append(SP).append(OPEN_ARRAY); + 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++) { - KeyValuePair kvp = kvpList.get(i); - sb.append(QUOTE).append(jsonSafeToString(kvp.key)).append(QUOTE); - sb.append(COL_SP); - sb.append(QUOTE).append(jsonSafeToString(kvp.value)).append(QUOTE); - - if(i != len) + for (int i = 0; i < len; i++) { + if (i != 0) sb.append(VALUE_SEPARATOR); + KeyValuePair kvp = kvpList.get(i); + sb.append(OPEN_OBJ); + appenderMember(sb, jsonEscapedToString(kvp.key), jsonEscapedToString(kvp.value)); + sb.append(CLOSE_OBJ); } sb.append(CLOSE_ARRAY); + sb.append(VALUE_SEPARATOR); } private void appendArgumentArray(StringBuilder sb, ILoggingEvent event) { Object[] argumentArray = event.getArgumentArray(); - if(argumentArray == null) + if (argumentArray == null) return; - sb.append(ARGUMENT_ARRAY_ATTR_NAME).append(ENTRY_SEPARATOR).append(SP).append(OPEN_ARRAY); + 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++) { - sb.append(QUOTE).append(jsonSafeToString(argumentArray[i])).append(QUOTE); - if(i != len) + for (int i = 0; i < len; i++) { + if(i != 0) sb.append(VALUE_SEPARATOR); + sb.append(QUOTE).append(jsonEscapedToString(argumentArray[i])).append(QUOTE); + } sb.append(CLOSE_ARRAY); + sb.append(VALUE_SEPARATOR); } private void appendMarkers(StringBuilder sb, ILoggingEvent event) { List markerList = event.getMarkerList(); - if(markerList == null) + if (markerList == null) return; - sb.append(MARKERS_ATTR_NAME).append(ENTRY_SEPARATOR).append(SP).append(OPEN_ARRAY); + 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++) { - sb.append(QUOTE).append(jsonSafeToString(markerList.get(i))).append(QUOTE); - if(i != len) + for (int i = 0; i < len; i++) { + if (i != 0) sb.append(VALUE_SEPARATOR); + sb.append(QUOTE).append(jsonEscapedToString(markerList.get(i))).append(QUOTE); + } sb.append(CLOSE_ARRAY); + sb.append(VALUE_SEPARATOR); + } - private String jsonSafeToString(Object o) { - if(o == null) + private String jsonEscapedToString(Object o) { + if (o == null) return NULL_STR; return jsonEscapeString(o.toString()); } - private String jsonSafeStr(String s) { - if(s == null) + private String nullSafeStr(String s) { + if (s == null) return NULL_STR; - return jsonEscapeString(s); + return s; } + private String jsonEscape(String s) { + if (s == null) + return NULL_STR; + return jsonEscapeString(s); + } private void appendMDC(StringBuilder sb, ILoggingEvent event) { Map map = event.getMDCPropertyMap(); - sb.append(MDC_ATTR_NAME).append(ENTRY_SEPARATOR).append(SP).append(OPEN_OBJ); - if(isNotEmptyMap(map)) { - map.entrySet().stream().forEach(e -> appendMapEntry(sb, e)); + sb.append(QUOTE).append(MDC_ATTR_NAME).append(QUOTE_COL).append(SP).append(OPEN_OBJ); + if (isNotEmptyMap(map)) { + Set> entrySet = map.entrySet(); + int i = 0; + for (Map.Entry entry : entrySet) { + if (i != 0) + sb.append(VALUE_SEPARATOR); + appenderMember(sb, jsonEscapedToString(entry.getKey()), jsonEscapedToString(entry.getValue())); + i++; + } + } sb.append(CLOSE_OBJ); + sb.append(VALUE_SEPARATOR); } - private void appendMapEntry(StringBuilder sb, Map.Entry entry) { - if(entry == null) - return; - - sb.append(QUOTE).append(jsonSafeToString(entry.getKey())).append(QUOTE).append(COL_SP).append(QUOTE) - .append(jsonSafeToString(entry.getValue())).append(QUOTE); - } - boolean isNotEmptyMap(Map map) { - if(map == null) - return false; - return !map.isEmpty(); + if (map == null) + return false; + return !map.isEmpty(); } @Override diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/spi/ClassPackagingData.java b/logback-classic/src/main/java/ch/qos/logback/classic/spi/ClassPackagingData.java index 1482c9fd3b..835655c188 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/spi/ClassPackagingData.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/spi/ClassPackagingData.java @@ -15,12 +15,17 @@ import java.io.Serializable; +@Deprecated public class ClassPackagingData implements Serializable { private static final long serialVersionUID = -804643281218337001L; - final String codeLocation; - final String version; - private final boolean exact; + String codeLocation; + String version; + boolean exact; + + public ClassPackagingData() { + + } public ClassPackagingData(String codeLocation, String version) { this.codeLocation = codeLocation; @@ -46,6 +51,18 @@ public boolean isExact() { return exact; } + public void setCodeLocation(String codeLocation) { + this.codeLocation = codeLocation; + } + + public void setVersion(String version) { + this.version = version; + } + + public void setExact(boolean exact) { + this.exact = exact; + } + @Override public int hashCode() { final int PRIME = 31; diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerContextVO.java b/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerContextVO.java index e1740d0974..efc22c65f2 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerContextVO.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerContextVO.java @@ -37,9 +37,9 @@ public class LoggerContextVO implements Serializable { private static final long serialVersionUID = 5488023392483144387L; - final String name; - final Map propertyMap; - final long birthTime; + protected String name; + protected Map propertyMap; + protected long birthTime; public LoggerContextVO(LoggerContext lc) { this.name = lc.getName(); diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/spi/StackTraceElementProxy.java b/logback-classic/src/main/java/ch/qos/logback/classic/spi/StackTraceElementProxy.java index f1f3876877..6e534b528e 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/spi/StackTraceElementProxy.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/spi/StackTraceElementProxy.java @@ -23,7 +23,9 @@ public class StackTraceElementProxy implements Serializable { // save a byte or two during serialization, as we can // reconstruct this field from 'ste' transient private String steAsString; - private ClassPackagingData cpd; + + @Deprecated + ClassPackagingData classPackagingData; public StackTraceElementProxy(StackTraceElement ste) { if (ste == null) { @@ -44,14 +46,14 @@ public StackTraceElement getStackTraceElement() { } public void setClassPackagingData(ClassPackagingData cpd) { - if (this.cpd != null) { + if (this.classPackagingData != null) { throw new IllegalStateException("Packaging data has been already set"); } - this.cpd = cpd; + this.classPackagingData = cpd; } public ClassPackagingData getClassPackagingData() { - return cpd; + return classPackagingData; } @Override @@ -72,11 +74,11 @@ public boolean equals(Object obj) { if (!ste.equals(other.ste)) { return false; } - if (cpd == null) { - if (other.cpd != null) { + if (classPackagingData == null) { + if (other.classPackagingData != null) { return false; } - } else if (!cpd.equals(other.cpd)) { + } else if (!classPackagingData.equals(other.classPackagingData)) { return false; } return true; diff --git a/logback-classic/src/test/input/fqcn.txt b/logback-classic/src/test/input/fqcn.txt index c5955a8732..a0234a76df 100644 --- a/logback-classic/src/test/input/fqcn.txt +++ b/logback-classic/src/test/input/fqcn.txt @@ -424,7 +424,7 @@ ch.qos.logback.classic.spi.BogusClassLoader ch.qos.logback.classic.spi.CallerDataTest ch.qos.logback.classic.spi.ContextListenerTest ch.qos.logback.classic.spi.CPDCSpecial -ch.qos.logback.classic.spi.DummyThrowableProxy +ch.qos.logback.classic.spi.PubThrowableProxy ch.qos.logback.classic.spi.LocalFirstClassLoader ch.qos.logback.classic.spi.LoggerComparatorTest ch.qos.logback.classic.spi.LoggingEventSerializationPerfTest diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/corpus/Corpus.java b/logback-classic/src/test/java/ch/qos/logback/classic/corpus/Corpus.java index 8fdb84622e..c33a99f07b 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/corpus/Corpus.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/corpus/Corpus.java @@ -22,6 +22,7 @@ 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.PubLoggerContextVO; import ch.qos.logback.classic.spi.PubLoggingEventVO; import ch.qos.logback.classic.spi.ThrowableProxyUtil; import ch.qos.logback.core.CoreConstants; @@ -71,7 +72,7 @@ static public ILoggingEvent[] makeStandardCorpus() throws IOException { } static public ILoggingEvent[] make(CorpusModel corpusModel, int n, boolean withCallerData) { - LoggerContextVO lcVO = corpusModel.getRandomlyNamedLoggerContextVO(); + PubLoggerContextVO lcVO = corpusModel.getRandomlyNamedLoggerContextVO(); PubLoggingEventVO[] plevoArray = new PubLoggingEventVO[n]; for (int i = 0; i < n; i++) { PubLoggingEventVO e = new PubLoggingEventVO(); diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/corpus/CorpusModel.java b/logback-classic/src/test/java/ch/qos/logback/classic/corpus/CorpusModel.java index 1bee80ed37..feabd024bf 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/corpus/CorpusModel.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/corpus/CorpusModel.java @@ -20,6 +20,7 @@ import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.spi.ClassPackagingData; import ch.qos.logback.classic.spi.LoggerContextVO; +import ch.qos.logback.classic.spi.PubLoggerContextVO; import ch.qos.logback.classic.spi.StackTraceElementProxy; import ch.qos.logback.classic.spi.ThrowableProxy; import ch.qos.logback.classic.spi.ThrowableProxyVO; @@ -167,10 +168,10 @@ public long getRandomTimeStamp() { return lastTimeStamp; } - LoggerContextVO getRandomlyNamedLoggerContextVO() { + PubLoggerContextVO getRandomlyNamedLoggerContextVO() { LoggerContext lc = new LoggerContext(); lc.setName(getRandomJavaIdentifier()); - return new LoggerContextVO(lc); + return new PubLoggerContextVO(lc); } String getRandomWord() { 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 new file mode 100644 index 0000000000..607ec75739 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/encoder/JsonEncoderTest.java @@ -0,0 +1,245 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2023, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v1.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.encoder; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.jsonTest.JsonLoggingEvent; +import ch.qos.logback.classic.jsonTest.JsonStringToLoggingEventMapper; +import ch.qos.logback.classic.jsonTest.ThrowableProxyComparator; +import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.core.testUtil.RandomUtil; +import com.fasterxml.jackson.core.JsonProcessingException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.Marker; +import org.slf4j.event.KeyValuePair; +import org.slf4j.helpers.BasicMarkerFactory; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class JsonEncoderTest { + + int diff = RandomUtil.getPositiveInt(); + + LoggerContext loggerContext = new LoggerContext(); + Logger logger = loggerContext.getLogger(JsonEncoderTest.class); + + JsonEncoder jsonEncoder = new JsonEncoder(); + + BasicMarkerFactory markerFactory = new BasicMarkerFactory(); + + Marker markerA = markerFactory.getMarker("A"); + + Marker markerB = markerFactory.getMarker("B"); + + JsonStringToLoggingEventMapper stringToLoggingEventMapper = new JsonStringToLoggingEventMapper(markerFactory); + + @BeforeEach + void setUp() { + loggerContext.setName("test_" + diff); + + jsonEncoder.setContext(loggerContext); + jsonEncoder.start(); + } + + @AfterEach + void tearDown() { + } + + @Test + void smoke() throws JsonProcessingException { + LoggingEvent event = new LoggingEvent("x", logger, Level.WARN, "hello", null, null); + + 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 + void contextWithProperties() throws JsonProcessingException { + loggerContext.putProperty("k", "v"); + loggerContext.putProperty("k"+diff, "v"+diff); + + LoggingEvent event = new LoggingEvent("x", logger, Level.WARN, "hello", null, null); + + 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); + + } + + private static void compareEvents(LoggingEvent event, JsonLoggingEvent resultEvent) { + assertEquals(event.getSequenceNumber(), resultEvent.getSequenceNumber()); + assertEquals(event.getTimeStamp(), resultEvent.getTimeStamp()); + assertEquals(event.getLevel(), resultEvent.getLevel()); + assertEquals(event.getLoggerName(), resultEvent.getLoggerName()); + assertEquals(event.getThreadName(), resultEvent.getThreadName()); + assertEquals(event.getMarkerList(), resultEvent.getMarkerList()); + assertEquals(event.getMDCPropertyMap(), resultEvent.getMDCPropertyMap()); + assertTrue(compareKeyValuePairLists(event.getKeyValuePairs(), resultEvent.getKeyValuePairs())); + + assertEquals(event.getLoggerContextVO(), resultEvent.getLoggerContextVO()); + assertTrue(ThrowableProxyComparator.areEqual(event.getThrowableProxy(), resultEvent.getThrowableProxy())); + + assertEquals(event.getMessage(), resultEvent.getMessage()); + assertTrue(Arrays.equals(event.getArgumentArray(), resultEvent.getArgumentArray())); + + } + + private static boolean compareKeyValuePairLists(List leftList, List rightList) { + if (leftList == rightList) + return true; + + if (leftList == null || rightList == null) + return false; + + int length = leftList.size(); + if (rightList.size() != length) { + System.out.println("length discrepancy"); + return false; + } + + //System.out.println("checking KeyValuePair lists"); + + for (int i = 0; i < length; i++) { + KeyValuePair leftKVP = leftList.get(i); + KeyValuePair rightKVP = rightList.get(i); + + boolean result = Objects.equals(leftKVP.key, rightKVP.key) && Objects.equals(leftKVP.value, rightKVP.value); + + if (!result) { + System.out.println("mismatch oin kvp " + leftKVP + " and " + rightKVP); + return false; + } + } + return true; + + } + + // private JsonLoggingEvent stringToJsonLoggingEvent(String resultString) throws JsonProcessingException { + // ObjectMapper objectMapper = new ObjectMapper(); + // JsonNode jsonNode = objectMapper.readTree(resultString); + // JsonLoggingEvent resultEvent = objectMapper.treeToValue(jsonNode, JsonLoggingEvent.class); + // String levelStr = jsonNode.at("/level").asText(); + // Level level = Level.toLevel(levelStr); + // + // JsonNode markersNode = jsonNode.at("/markers"); + // if(markersNode!=null && markersNode.isArray()) { + // List markerList = new ArrayList<>(); + // Iterator itr = markersNode.iterator(); + // while (itr.hasNext()) { + // JsonNode item=itr.next(); + // String markerStr = item.asText(); + // Marker marker = markerFactory.getMarker(markerStr); + // markerList.add(marker); + // } + // resultEvent.markerList = markerList; + // } + // + // resultEvent.level = level; + // return resultEvent; + // } + + @Test + void withMarkers() throws JsonProcessingException { + LoggingEvent event = new LoggingEvent("x", logger, Level.WARN, "hello", null, null); + event.addMarker(markerA); + event.addMarker(markerB); + + 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 + void withArguments() throws JsonProcessingException { + LoggingEvent event = new LoggingEvent("x", logger, Level.WARN, "hello", null, new Object[] { "arg1", "arg2" }); + + 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 + void withKeyValuePairs() throws JsonProcessingException { + LoggingEvent event = new LoggingEvent("x", logger, Level.WARN, "hello kvp", null, + new Object[] { "arg1", "arg2" }); + event.addKeyValuePair(new KeyValuePair("k1", "v1")); + event.addKeyValuePair(new KeyValuePair("k2", "v2")); + + 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 + void withMDC() throws JsonProcessingException { + Map map = new HashMap<>(); + map.put("key", "value"); + map.put("a", "b"); + + LoggingEvent event = new LoggingEvent("x", logger, Level.WARN, "hello kvp", null, + new Object[] { "arg1", "arg2" }); + Map mdcMap = new HashMap<>(); + mdcMap.put("mdcK1", "v1"); + mdcMap.put("mdcK2", "v2"); + + event.setMDCPropertyMap(mdcMap); + + 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 + void withThrowable() throws JsonProcessingException { + Throwable t = new RuntimeException("test"); + LoggingEvent event = new LoggingEvent("in withThrowable test", logger, Level.WARN, "hello kvp", t, null); + + 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); + } +} \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/html/HTMLLayoutTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/html/HTMLLayoutTest.java index 291a6b6ddb..273e9ba0bc 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/html/HTMLLayoutTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/html/HTMLLayoutTest.java @@ -30,7 +30,7 @@ import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.joran.JoranConfigurator; -import ch.qos.logback.classic.spi.DummyThrowableProxy; +import ch.qos.logback.classic.spi.PubThrowableProxy; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.LoggingEvent; import ch.qos.logback.classic.spi.StackTraceElementProxy; @@ -104,7 +104,7 @@ public void testPresentationHeader() throws Exception { @Test public void testAppendThrowable() throws Exception { StringBuilder buf = new StringBuilder(); - DummyThrowableProxy tp = new DummyThrowableProxy(); + PubThrowableProxy tp = new PubThrowableProxy(); tp.setClassName("test1"); tp.setMessage("msg1"); diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/JsonLoggingEvent.java b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/JsonLoggingEvent.java new file mode 100644 index 0000000000..5feb5e90a5 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/JsonLoggingEvent.java @@ -0,0 +1,198 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2023, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v1.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.jsonTest; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.encoder.JsonEncoder; +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.PubLoggerContextVO; +import ch.qos.logback.classic.spi.PubThrowableProxy; +import com.fasterxml.jackson.annotation.JsonAlias; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import org.slf4j.Marker; +import org.slf4j.event.KeyValuePair; +import org.slf4j.helpers.MessageFormatter; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@JsonIgnoreProperties({ }) +public class JsonLoggingEvent implements ILoggingEvent { + public String threadName; + public String loggerName; + + + @JsonAlias({"context"}) + public LoggerContextVO loggerContextVO; + + public Level level; + public String message; + + private transient String formattedMessage; + + @JsonAlias({"arguments"}) + public Object[] argumentArray; + + @JsonAlias({"throwable"}) + public PubThrowableProxy throwableProxy; + + @JsonIgnore + public StackTraceElement[] callerDataArray; + + @JsonAlias({"markers"}) + public List markerList; + + @JsonAlias({"kvp"}) + public List kvpList; + + @JsonAlias({"mdc"}) + public Map mdcPropertyMap; + + public long timestamp; + public int nanoseconds; + public long sequenceNumber; + + public String getThreadName() { + return threadName; + } + + + public LoggerContextVO getLoggerContextVO() { + return loggerContextVO; + } + + public String getLoggerName() { + return loggerName; + } + + + //@JsonIgnore + public Level getLevel() { + return level; + } + + //@JsonIgnore + public void setLevel(Level aLavel) { + level = aLavel; + } + + public String getMessage() { + return message; + } + + public String getFormattedMessage() { + if (formattedMessage != null) { + return formattedMessage; + } + + if (argumentArray != null) { + formattedMessage = MessageFormatter.arrayFormat(message, argumentArray).getMessage(); + } else { + formattedMessage = message; + } + + return formattedMessage; + } + + public Object[] getArgumentArray() { + return argumentArray; + } + + public IThrowableProxy getThrowableProxy() { + return throwableProxy; + } + + public StackTraceElement[] getCallerData() { + return callerDataArray; + } + + public boolean hasCallerData() { + return callerDataArray != null; + } + + public List getMarkerList() { + return markerList; + } + + public long getTimeStamp() { + return timestamp; + } + + @Override + public int getNanoseconds() { + return nanoseconds; + } + + public long getSequenceNumber() { + return sequenceNumber; + } + + public void setSequenceNumber(long sequenceNumber) { + this.sequenceNumber = sequenceNumber; + } + + public long getContextBirthTime() { + return loggerContextVO.getBirthTime(); + } + + public LoggerContextVO getContextLoggerRemoteView() { + return loggerContextVO; + } + + public Map getMDCPropertyMap() { + return mdcPropertyMap; + } + + public Map getMdc() { + return mdcPropertyMap; + } + + public void setMdc( Map map) { + mdcPropertyMap = map; + } + + public void prepareForDeferredProcessing() { + } + + @Override + public List getKeyValuePairs() { + return kvpList; + } + + public void setKeyValuePairs( List aList) { + kvpList = aList; + } + + + + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(timestamp); + sb.append(" "); + sb.append(level); + sb.append(" ["); + sb.append(threadName); + sb.append("] "); + sb.append(loggerName); + sb.append(" - "); + sb.append(getFormattedMessage()); + return sb.toString(); + } + +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/JsonStringToLoggingEventMapper.java b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/JsonStringToLoggingEventMapper.java new file mode 100644 index 0000000000..ce2370ab21 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/JsonStringToLoggingEventMapper.java @@ -0,0 +1,104 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2023, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v1.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.jsonTest; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.encoder.JsonEncoder; +import ch.qos.logback.classic.spi.LoggerContextVO; +import ch.qos.logback.classic.spi.StackTraceElementProxy; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import org.slf4j.IMarkerFactory; +import org.slf4j.Marker; +import org.slf4j.event.KeyValuePair; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +public class JsonStringToLoggingEventMapper { + IMarkerFactory markerFactory; + + + public JsonStringToLoggingEventMapper(IMarkerFactory markerFactory) { + this.markerFactory = markerFactory; + } + + public JsonLoggingEvent mapStringToLoggingEvent(String resultString) throws JsonProcessingException { + ObjectMapper objectMapper = new ObjectMapper(); + SimpleModule module = new SimpleModule(); + module.addDeserializer(StackTraceElementProxy.class, new STEPDeserializer()); + module.addDeserializer(Level.class, new LevelDeserializer()); + module.addDeserializer(Marker.class, new MarkerDeserializer(markerFactory)); + module.addDeserializer(KeyValuePair.class, new KeyValuePairDeserializer()); + module.addDeserializer(LoggerContextVO.class, new LoggerContextVODeserializer()); + + objectMapper.registerModule(module); + + JsonNode jsonNode = objectMapper.readTree(resultString); + JsonLoggingEvent resultEvent = objectMapper.treeToValue(jsonNode, JsonLoggingEvent.class); + //buildLevel(jsonNode, resultEvent); + + //xbuildMarkersList(jsonNode, resultEvent); + //xbuildKVPList(jsonNode, resultEvent); + //buildThrowableProxy(jsonNode, resultEvent); + return resultEvent; + } + + private static void UNUSED_buildLevel(JsonNode jsonNode, JsonLoggingEvent resultEvent) { + String levelStr = jsonNode.at("/"+ JsonEncoder.LEVEL_ATTR_NAME).asText(); + Level level = Level.toLevel(levelStr); + resultEvent.level = level; + } + + private void UNUSED_buildMarkersList(JsonNode jsonNode, JsonLoggingEvent resultEvent) { + JsonNode markersNode = jsonNode.at("/"+JsonEncoder.MARKERS_ATTR_NAME); + if(markersNode!=null && markersNode.isArray()) { + List markerList = new ArrayList<>(); + Iterator itr = markersNode.iterator(); + while (itr.hasNext()) { + JsonNode item=itr.next(); + String markerStr = item.asText(); + Marker marker = markerFactory.getMarker(markerStr); + markerList.add(marker); + } + resultEvent.markerList = markerList; + } + } + + + private void UNUSED_buildKVPList(JsonNode jsonNode, JsonLoggingEvent resultEvent) { + JsonNode kvpNode = jsonNode.at("/"+JsonEncoder.KEY_VALUE_PAIRS_ATTR_NAME); + if(kvpNode!=null && kvpNode.isArray()) { + System.out.println("in buildKVPList"); + List kvpList = new ArrayList<>(); + Iterator itr = kvpNode.iterator(); + while (itr.hasNext()) { + JsonNode item=itr.next(); + + Map.Entry entry = item.fields().next(); + String key = entry.getKey(); + String val = entry.getValue().asText(); + kvpList.add(new KeyValuePair(key, val)); + + } + resultEvent.kvpList =kvpList; + } + } + +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/KeyValuePairDeserializer.java b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/KeyValuePairDeserializer.java new file mode 100644 index 0000000000..d3d15dbce9 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/KeyValuePairDeserializer.java @@ -0,0 +1,58 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2023, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v1.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.jsonTest; + +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import org.slf4j.event.KeyValuePair; + +import java.io.IOException; +import java.util.Iterator; +import java.util.Map; + +public class KeyValuePairDeserializer extends StdDeserializer { + public KeyValuePairDeserializer() { + this(null); + } + + + public KeyValuePairDeserializer(Class vc) { + super(vc); + } + + + @Override + public KeyValuePair deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) + throws IOException, JacksonException { + + JsonNode node = jsonParser.getCodec().readTree(jsonParser); + + if(node.isObject()) { + Iterator> it = node.fields(); + if(it.hasNext()) { + Map.Entry entry = it.next(); + String key = entry.getKey(); + String value = entry.getValue().asText(); + KeyValuePair kvp = new KeyValuePair(key, value); + return kvp; + } + } + + return null; + } +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/LevelDeserializer.java b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/LevelDeserializer.java new file mode 100644 index 0000000000..11d5568f1b --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/LevelDeserializer.java @@ -0,0 +1,44 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2023, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v1.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.jsonTest; + +import ch.qos.logback.classic.Level; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; + +import java.io.IOException; + +public class LevelDeserializer extends StdDeserializer { + + public LevelDeserializer() { + this(null); + } + + public LevelDeserializer(Class vc) { + super(vc); + } + + @Override + public Level deserialize(JsonParser jp, DeserializationContext ctxt) + throws IOException, JsonProcessingException { + JsonNode node = jp.getCodec().readTree(jp); + String levelStr = node.asText(); + Level level = Level.toLevel(levelStr); + return level; + } +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/LoggerContextVODeserializer.java b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/LoggerContextVODeserializer.java new file mode 100644 index 0000000000..56cea0c097 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/LoggerContextVODeserializer.java @@ -0,0 +1,65 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2023, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v1.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.jsonTest; + +import ch.qos.logback.classic.encoder.JsonEncoder; +import ch.qos.logback.classic.spi.LoggerContextVO; +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import org.slf4j.event.KeyValuePair; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +public class LoggerContextVODeserializer extends StdDeserializer { + + public LoggerContextVODeserializer() { + this(null); + } + + public LoggerContextVODeserializer(Class vc) { + super(vc); + } + + @Override + public LoggerContextVO deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) + throws IOException, JacksonException { + + JsonNode node = jsonParser.getCodec().readTree(jsonParser); + if(node.isObject()) { + JsonNode nameNode = node.get(JsonEncoder.NAME_ATTR_NAME); + String name = nameNode.asText(); + JsonNode bdayNode = node.get(JsonEncoder.BIRTHDATE_ATTR_NAME); + long birthday = bdayNode.asLong(); + + JsonNode propertiesNode = node.get(JsonEncoder.CONTEXT_PROPERTIES_ATTR_NAME); + Map propertiesMap = new HashMap<>(); + Iterator> it = propertiesNode.fields(); + while(it.hasNext()) { + Map.Entry entry = it.next(); + String key = entry.getKey(); + String value = entry.getValue().asText(); + propertiesMap.put(key, value); + } + return new LoggerContextVO(name, propertiesMap, birthday); + } + return null; + } +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/MarkerDeserializer.java b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/MarkerDeserializer.java new file mode 100644 index 0000000000..6a54815f45 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/MarkerDeserializer.java @@ -0,0 +1,50 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2023, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v1.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.jsonTest; + +import ch.qos.logback.classic.Level; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import org.slf4j.IMarkerFactory; +import org.slf4j.Marker; +import org.slf4j.MarkerFactory; + +import java.io.IOException; + +public class MarkerDeserializer extends StdDeserializer { + + IMarkerFactory markerFactory; + + public MarkerDeserializer(IMarkerFactory markerFactory) { + this(null, markerFactory); + } + + public MarkerDeserializer(Class vc, IMarkerFactory markerFactory) { + super(vc); + this.markerFactory = markerFactory; + } + + @Override + public Marker deserialize(JsonParser jp, DeserializationContext ctxt) + throws IOException, JsonProcessingException { + JsonNode node = jp.getCodec().readTree(jp); + String markerStr = node.asText(); + Marker marker = markerFactory.getMarker(markerStr); + return marker; + } +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/STEPDeserializer.java b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/STEPDeserializer.java new file mode 100644 index 0000000000..788d75ca82 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/STEPDeserializer.java @@ -0,0 +1,50 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2023, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v1.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.jsonTest; + +import ch.qos.logback.classic.spi.StackTraceElementProxy; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.node.IntNode; + +import java.io.IOException; + +public class STEPDeserializer extends StdDeserializer { + + public STEPDeserializer() { + this(null); + } + + public STEPDeserializer(Class vc) { + super(vc); + } + + @Override + public StackTraceElementProxy deserialize(JsonParser jp, DeserializationContext ctxt) + throws IOException, JsonProcessingException { + JsonNode node = jp.getCodec().readTree(jp); + String className = node.get("className").asText(); + String methodName = node.get("methodName").asText(); + String fileName = node.get("fileName").asText(); + + int lineNumber = (Integer) ((IntNode) node.get("lineNumber")).numberValue(); + + StackTraceElement ste = new StackTraceElement(className, methodName, fileName, lineNumber); + return new StackTraceElementProxy(ste); + } +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/ThrowableProxyComparator.java b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/ThrowableProxyComparator.java new file mode 100644 index 0000000000..3e951985da --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/ThrowableProxyComparator.java @@ -0,0 +1,110 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2023, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v1.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.jsonTest; + +import ch.qos.logback.classic.spi.IThrowableProxy; +import ch.qos.logback.classic.spi.StackTraceElementProxy; + +import java.util.Arrays; +import java.util.Objects; + +public class ThrowableProxyComparator { + + + static public boolean areEqual(IThrowableProxy left, IThrowableProxy right) { + + if(left == right) + return true; + + if(left == null) + return false; + + if(!left.getClassName().equals(right.getClassName())) + return false; + + + + if(!left.getMessage().equals(right.getMessage())) + return false; + + + StackTraceElementProxy[] leftStepArray = left.getStackTraceElementProxyArray(); + StackTraceElementProxy[] rightStepArray = right.getStackTraceElementProxyArray(); + + System.out.println("before equalsSTEPArray"); + + if(!equalsSTEPArray(leftStepArray, rightStepArray)) + return false; + + + return true; + } + + static public boolean equalsSTEPArray( StackTraceElementProxy[] leftStepArray, StackTraceElementProxy[] rightStepArray) { + if (leftStepArray==rightStepArray) + return true; + if (leftStepArray==null || rightStepArray==null) + return false; + + int length = leftStepArray.length; + if (rightStepArray.length != length) { + System.out.println("length discrepancy"); + return false; + } + + System.out.println("checking ste array elements"); + + for (int i=0; i propertyMap, long birthTime) { + super(name, propertyMap, birthTime); + } + + public void setName(String name) { + this.name = name; + } + + public void setPropertyMap(Map propertyMap) { + this.propertyMap = propertyMap; + } + + + public void setBirthTime(long birthTime) { + this.birthTime = birthTime; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + PubLoggerContextVO that = (PubLoggerContextVO) o; + return birthTime == that.birthTime && Objects.equals(name, that.name) && Objects.equals(propertyMap, + that.propertyMap); + } + + @Override + public int hashCode() { + return Objects.hash(name, propertyMap, birthTime); + } +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/spi/PubLoggingEventVO.java b/logback-classic/src/test/java/ch/qos/logback/classic/spi/PubLoggingEventVO.java index 3e39df300a..dc07a78680 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/spi/PubLoggingEventVO.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/spi/PubLoggingEventVO.java @@ -20,6 +20,10 @@ import java.util.List; import java.util.Map; +import com.fasterxml.jackson.annotation.JacksonInject; +import com.fasterxml.jackson.annotation.JsonAlias; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.databind.annotation.JsonValueInstantiator; import org.slf4j.Marker; import org.slf4j.event.KeyValuePair; import org.slf4j.helpers.MessageFormatter; @@ -40,13 +44,14 @@ public class PubLoggingEventVO implements ILoggingEvent, Serializable { public String threadName; public String loggerName; - public LoggerContextVO loggerContextVO; + public PubLoggerContextVO loggerContextVO; public transient Level level; public String message; private transient String formattedMessage; + @JsonAlias public Object[] argumentArray; public IThrowableProxy throwableProxy; @@ -70,6 +75,8 @@ public String getLoggerName() { return loggerName; } + + @JsonIgnore public Level getLevel() { return level; } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/spi/DummyThrowableProxy.java b/logback-classic/src/test/java/ch/qos/logback/classic/spi/PubThrowableProxy.java similarity index 86% rename from logback-classic/src/test/java/ch/qos/logback/classic/spi/DummyThrowableProxy.java rename to logback-classic/src/test/java/ch/qos/logback/classic/spi/PubThrowableProxy.java index 055d41f796..1f62677504 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/spi/DummyThrowableProxy.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/spi/PubThrowableProxy.java @@ -1,6 +1,6 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2023, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under * either the terms of the Eclipse Public License v1.0 as published by @@ -13,11 +13,17 @@ */ package ch.qos.logback.classic.spi; -public class DummyThrowableProxy implements IThrowableProxy { +import ch.qos.logback.classic.spi.IThrowableProxy; +import ch.qos.logback.classic.spi.StackTraceElementProxy; +import com.fasterxml.jackson.annotation.JsonAlias; + +public class PubThrowableProxy implements IThrowableProxy { private String className; private String message; private int commonFramesCount; + + @JsonAlias("stepArray") private StackTraceElementProxy[] stackTraceElementProxyArray; private IThrowableProxy cause; private IThrowableProxy[] suppressed; diff --git a/pom.xml b/pom.xml index e96ae1f5cc..511a733ca1 100755 --- a/pom.xml +++ b/pom.xml @@ -75,6 +75,7 @@ 1.1.0 10.0.10 11.0.12 + 2.15.0 2.4.0