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