diff --git a/pom.xml b/pom.xml
index 59a4286..7e0c704 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
io.kokuwa.micronaut
micronaut-logging
- 3.1.1-SNAPSHOT
+ 3.2.0-SNAPSHOT
Logging Support for Micronaut
Enhanced logging using MDC or request header.
@@ -101,8 +101,8 @@
1.2.12
- 0.1.5
- 3.1.1-SNAPSHOT
+ 2.15.0
+ 3.2.0-SNAPSHOT
3.9.1
3.11.0
1.7.36
@@ -138,19 +138,9 @@
${version.ch.qos.logback}
- ch.qos.logback.contrib
- logback-json-classic
- ${version.ch.qos.logback.contrib}
-
-
- ch.qos.logback.contrib
- logback-json-core
- ${version.ch.qos.logback.contrib}
-
-
- ch.qos.logback.contrib
- logback-jackson
- ${version.ch.qos.logback.contrib}
+ com.fasterxml.jackson.core
+ jackson-databind
+ ${version.com.fasterxml.jackson}
@@ -217,13 +207,9 @@
logback-classic
- ch.qos.logback.contrib
- logback-json-classic
-
-
- ch.qos.logback.contrib
- logback-jackson
- runtime
+ com.fasterxml.jackson.core
+ jackson-databind
+ provided
diff --git a/src/it/log-gcp-with-service/postbuild.bsh b/src/it/log-gcp-with-service/postbuild.bsh
index 7ba1cab..6264c10 100644
--- a/src/it/log-gcp-with-service/postbuild.bsh
+++ b/src/it/log-gcp-with-service/postbuild.bsh
@@ -1,6 +1,6 @@
// verify log
-String expected = "^\\{\"time\":\"202[3-9]-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{0,3}Z\",\"severity\":\"INFO\",\"thread\":\"main\",\"logger\":\"io.kokuwa.micronaut.logging.LoggingTest\",\"message\":\"test-output-marker\",\"raw-message\":\"test-output-marker\",\"serviceContext\":\\{\"version\":\"0.1.2\",\"service\":\"test-service\"}}$";
+String expected = "^\\{\"time\":\"202[3-9]-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{0,3}Z\",\"severity\":\"INFO\",\"thread\":\"main\",\"logger\":\"io.kokuwa.micronaut.logging.LoggingTest\",\"message\":\"test-output-marker\",\"raw-message\":\"test-output-marker\",\"serviceContext\":\\{\"service\":\"test-service\",\"version\":\"0.1.2\"}}$";
String[] logs = org.codehaus.plexus.util.FileUtils.fileRead(basedir + "/build.log").split("\n");
for (String log : logs) {
diff --git a/src/main/java/io/kokuwa/micronaut/logging/layout/GcpJsonLayout.java b/src/main/java/io/kokuwa/micronaut/logging/layout/GcpJsonLayout.java
index 139607c..524601c 100644
--- a/src/main/java/io/kokuwa/micronaut/logging/layout/GcpJsonLayout.java
+++ b/src/main/java/io/kokuwa/micronaut/logging/layout/GcpJsonLayout.java
@@ -1,12 +1,10 @@
package io.kokuwa.micronaut.logging.layout;
import java.time.Instant;
-import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import ch.qos.logback.classic.spi.ILoggingEvent;
-import ch.qos.logback.contrib.json.classic.JsonLayout;
import io.micronaut.core.util.StringUtils;
/**
@@ -44,7 +42,7 @@ public class GcpJsonLayout extends JsonLayout {
private void addServiceContext(Map map) {
if (serviceContext == null) {
- serviceContext = new HashMap<>(2);
+ serviceContext = new LinkedHashMap<>(2);
if (StringUtils.isNotEmpty(serviceName) && !serviceName.endsWith(UNDEFINED)) {
serviceContext.put("service", serviceName);
}
diff --git a/src/main/java/io/kokuwa/micronaut/logging/layout/JsonLayout.java b/src/main/java/io/kokuwa/micronaut/logging/layout/JsonLayout.java
new file mode 100644
index 0000000..9617549
--- /dev/null
+++ b/src/main/java/io/kokuwa/micronaut/logging/layout/JsonLayout.java
@@ -0,0 +1,182 @@
+package io.kokuwa.micronaut.logging.layout;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.TimeZone;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import ch.qos.logback.classic.pattern.ThrowableHandlingConverter;
+import ch.qos.logback.classic.pattern.ThrowableProxyConverter;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.CoreConstants;
+import ch.qos.logback.core.LayoutBase;
+import io.micronaut.http.MediaType;
+
+public class JsonLayout extends LayoutBase {
+
+ public static final String TIMESTAMP_ATTR_NAME = "timestamp";
+ public static final String LEVEL_ATTR_NAME = "level";
+ public static final String THREAD_ATTR_NAME = "thread";
+ public static final String MDC_ATTR_NAME = "mdc";
+ public static final String LOGGER_ATTR_NAME = "logger";
+ public static final String FORMATTED_MESSAGE_ATTR_NAME = "message";
+ public static final String MESSAGE_ATTR_NAME = "raw-message";
+ public static final String EXCEPTION_ATTR_NAME = "exception";
+ public static final String CONTEXT_ATTR_NAME = "context";
+
+ private final ObjectMapper mapper = new ObjectMapper();
+
+ protected boolean includeLevel = true;
+ protected boolean includeThreadName = true;
+ protected boolean includeMDC = true;
+ protected boolean includeLoggerName = true;
+ protected boolean includeFormattedMessage = true;
+ protected boolean includeMessage = true;
+ protected boolean includeException = true;
+ protected boolean includeContextName = false;
+ protected boolean includeTimestamp = true;
+ private String timestampFormat;
+ private String timestampFormatTimezoneId;
+ private ThrowableHandlingConverter throwableHandlingConverter = new ThrowableProxyConverter();
+
+ @Override
+ public String getContentType() {
+ return MediaType.APPLICATION_JSON;
+ }
+
+ @Override
+ public void start() {
+ this.throwableHandlingConverter.start();
+ super.start();
+ }
+
+ @Override
+ public void stop() {
+ super.stop();
+ this.throwableHandlingConverter.stop();
+ }
+
+ @Override
+ public String doLayout(ILoggingEvent event) {
+ var map = toJsonMap(event);
+ try {
+ return mapper.writeValueAsString(map) + CoreConstants.LINE_SEPARATOR;
+ } catch (JsonProcessingException e) {
+ addError("Failed to write json from event " + event + " and map " + map, e);
+ return null;
+ }
+ }
+
+ protected Map toJsonMap(ILoggingEvent event) {
+ var map = new LinkedHashMap();
+ addTimestamp(TIMESTAMP_ATTR_NAME, includeTimestamp, event.getTimeStamp(), map);
+ add(LEVEL_ATTR_NAME, includeLevel, String.valueOf(event.getLevel()), map);
+ add(THREAD_ATTR_NAME, includeThreadName, event.getThreadName(), map);
+ addMap(MDC_ATTR_NAME, includeMDC, event.getMDCPropertyMap(), map);
+ add(LOGGER_ATTR_NAME, includeLoggerName, event.getLoggerName(), map);
+ add(FORMATTED_MESSAGE_ATTR_NAME, includeFormattedMessage, event.getFormattedMessage(), map);
+ add(MESSAGE_ATTR_NAME, includeMessage, event.getMessage(), map);
+ add(CONTEXT_ATTR_NAME, includeContextName, event.getLoggerContextVO().getName(), map);
+ addThrowableInfo(EXCEPTION_ATTR_NAME, includeException, event, map);
+ return map;
+ }
+
+ protected void addThrowableInfo(String fieldName, boolean field, ILoggingEvent value, Map map) {
+ if (field && value != null) {
+ var throwableProxy = value.getThrowableProxy();
+ if (throwableProxy != null) {
+ var ex = throwableHandlingConverter.convert(value);
+ if (ex != null && !ex.equals("")) {
+ map.put(fieldName, ex);
+ }
+ }
+ }
+ }
+
+ protected void addMap(String key, boolean field, Map mapValue, Map map) {
+ if (field && mapValue != null && !mapValue.isEmpty()) {
+ map.put(key, mapValue);
+ }
+ }
+
+ protected void addTimestamp(String key, boolean field, long timeStamp, Map map) {
+ if (field) {
+ var formatted = formatTimestamp(timeStamp);
+ if (formatted != null) {
+ map.put(key, formatted);
+ }
+ }
+ }
+
+ protected void add(String fieldName, boolean field, String value, Map map) {
+ if (field && value != null) {
+ map.put(fieldName, value);
+ }
+ }
+
+ protected String formatTimestamp(long timestamp) {
+ if (timestampFormat == null || timestamp < 0) {
+ return String.valueOf(timestamp);
+ }
+ var date = new Date(timestamp);
+ var format = new SimpleDateFormat(timestampFormat);
+ if (timestampFormatTimezoneId != null) {
+ format.setTimeZone(TimeZone.getTimeZone(timestampFormatTimezoneId));
+ }
+ return format.format(date);
+ }
+
+ // setter
+
+ public void setIncludeLevel(boolean includeLevel) {
+ this.includeLevel = includeLevel;
+ }
+
+ public void setIncludeThreadName(boolean includeThreadName) {
+ this.includeThreadName = includeThreadName;
+ }
+
+ public void setIncludeMDC(boolean includeMDC) {
+ this.includeMDC = includeMDC;
+ }
+
+ public void setIncludeLoggerName(boolean includeLoggerName) {
+ this.includeLoggerName = includeLoggerName;
+ }
+
+ public void setIncludeFormattedMessage(boolean includeFormattedMessage) {
+ this.includeFormattedMessage = includeFormattedMessage;
+ }
+
+ public void setIncludeMessage(boolean includeMessage) {
+ this.includeMessage = includeMessage;
+ }
+
+ public void setIncludeException(boolean includeException) {
+ this.includeException = includeException;
+ }
+
+ public void setIncludeContextName(boolean includeContextName) {
+ this.includeContextName = includeContextName;
+ }
+
+ public void setIncludeTimestamp(boolean includeTimestamp) {
+ this.includeTimestamp = includeTimestamp;
+ }
+
+ public void setTimestampFormat(String timestampFormat) {
+ this.timestampFormat = timestampFormat;
+ }
+
+ public void setTimestampFormatTimezoneId(String timestampFormatTimezoneId) {
+ this.timestampFormatTimezoneId = timestampFormatTimezoneId;
+ }
+
+ public void setThrowableHandlingConverter(ThrowableHandlingConverter throwableHandlingConverter) {
+ this.throwableHandlingConverter = throwableHandlingConverter;
+ }
+}
diff --git a/src/main/resources/io/kokuwa/logback/appender-gcp.xml b/src/main/resources/io/kokuwa/logback/appender-gcp.xml
index e847d99..a36210d 100644
--- a/src/main/resources/io/kokuwa/logback/appender-gcp.xml
+++ b/src/main/resources/io/kokuwa/logback/appender-gcp.xml
@@ -6,10 +6,6 @@
${SERVICE_NAME}
${SERVICE_VERSION}
-
- true
- true
- false
diff --git a/src/main/resources/io/kokuwa/logback/appender-json.xml b/src/main/resources/io/kokuwa/logback/appender-json.xml
index b850ac7..e0034e4 100644
--- a/src/main/resources/io/kokuwa/logback/appender-json.xml
+++ b/src/main/resources/io/kokuwa/logback/appender-json.xml
@@ -3,12 +3,7 @@
-
-
- true
- true
- false
-
+