diff --git a/cf-java-logging-support-logback/pom.xml b/cf-java-logging-support-logback/pom.xml index 13a6449..502a87f 100644 --- a/cf-java-logging-support-logback/pom.xml +++ b/cf-java-logging-support-logback/pom.xml @@ -25,6 +25,11 @@ cf-java-logging-support-core ${project.version} + + org.springframework.boot + spring-boot + ${springboot.version} + com.sap.hcp.cf.logging cf-java-logging-support-core diff --git a/cf-java-logging-support-logback/src/main/java/com/sap/hcp/cf/logback/boot/CloudFoundryStructuredLogFormatter.java b/cf-java-logging-support-logback/src/main/java/com/sap/hcp/cf/logback/boot/CloudFoundryStructuredLogFormatter.java new file mode 100644 index 0000000..f399a7c --- /dev/null +++ b/cf-java-logging-support-logback/src/main/java/com/sap/hcp/cf/logback/boot/CloudFoundryStructuredLogFormatter.java @@ -0,0 +1,39 @@ +package com.sap.hcp.cf.logback.boot; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import com.sap.hcp.cf.logback.encoder.JsonEncoder; +import org.springframework.boot.logging.structured.StructuredLogFormatter; + +/** + * A Logback events formatter for structured logging in SpringBoot + * configuration, suited + * for Cloud Foundry applications. This class can be used as a value for + * {@code logging.structured.format.console} + * or @{code logging.structured.format.file} in a SpringBoot applications. + *

+ * This is a simple wrapper around the {@code JsonEncoder} created for Logback. + * This formatter does not accept any configuration parameters and uses the default + * settings coming with JsonEncoder. The JSON output will contain the same fields + * as produced with {@code JsonEncoder}, but you don't have to use logback.xml + * (or logback-spring.xml) to use it. + *

+ * Example usage with SpringBoot ({@code application.properties}): + * + *

+ * logging.structured.format.console = com.sap.hcp.cf.logback.boot.CloudFoundryStructuredLogFormatter
+ * 
+ */ +public class CloudFoundryStructuredLogFormatter implements StructuredLogFormatter { + + private final JsonEncoder jsonEncoder; + + public CloudFoundryStructuredLogFormatter() { + this.jsonEncoder = JsonEncoder.createStarted(); + } + + @Override + public String format(ILoggingEvent event) { + return jsonEncoder.getJson(event); + } + +} diff --git a/cf-java-logging-support-logback/src/main/java/com/sap/hcp/cf/logback/encoder/JsonEncoder.java b/cf-java-logging-support-logback/src/main/java/com/sap/hcp/cf/logback/encoder/JsonEncoder.java index 45efe06..dd28641 100644 --- a/cf-java-logging-support-logback/src/main/java/com/sap/hcp/cf/logback/encoder/JsonEncoder.java +++ b/cf-java-logging-support-logback/src/main/java/com/sap/hcp/cf/logback/encoder/JsonEncoder.java @@ -21,7 +21,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.Marker; - import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.jr.ob.JSON; import com.fasterxml.jackson.jr.ob.JSON.Builder; @@ -78,6 +77,19 @@ public JsonEncoder() { logbackContextFieldSuppliers.add(new RequestRecordFieldSupplier()); } + /** + * Creates a new encoder instance in the {@code started} state, with default settings. + * This means that the encoder is ready to encode log events once this method returns. + * + * @return a new {@link JsonEncoder} instance in the started state + */ + public static JsonEncoder createStarted() { + JsonEncoder encoder = new JsonEncoder(); + encoder.start(); + + return encoder; + } + /** *

* Adds a field to the "#cf" object in the generated output. If the log @@ -286,7 +298,11 @@ public byte[] encode(ILoggingEvent event) { return getJson(event).getBytes(charset); } - private String getJson(ILoggingEvent event) { + public String getJson(ILoggingEvent event) { + if(!isStarted()) { + throw new IllegalStateException("Encoder is not started. Please call start() before encoding."); + } + try (StringWriter writer = new StringWriter()) { ObjectComposer> oc = json.composeTo(writer).startObject(); addMarkers(oc, event); diff --git a/cf-java-logging-support-logback/src/test/java/com/sap/hcp/cf/logback/boot/CloudFoundryStructuredLogFormatterTest.java b/cf-java-logging-support-logback/src/test/java/com/sap/hcp/cf/logback/boot/CloudFoundryStructuredLogFormatterTest.java new file mode 100644 index 0000000..5abd292 --- /dev/null +++ b/cf-java-logging-support-logback/src/test/java/com/sap/hcp/cf/logback/boot/CloudFoundryStructuredLogFormatterTest.java @@ -0,0 +1,32 @@ +package com.sap.hcp.cf.logback.boot; + +import static org.junit.Assert.assertTrue; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.spi.LoggingEvent; +import org.junit.Test; + +public class CloudFoundryStructuredLogFormatterTest { + + CloudFoundryStructuredLogFormatter underTest = new CloudFoundryStructuredLogFormatter(); + + @Test + public void testFormat() throws Exception { + LoggingEvent event = new LoggingEvent(); + event.setLevel(Level.DEBUG); + event.setLoggerName("my-logger"); + event.setMessage("This is some very important log message"); + event.setThreadName("thread-1"); + + String result = underTest.format(event); + + assertTrue(result.startsWith("{")); + assertTrue(result.endsWith("}\n")); + assertTrue(result.contains("\"level\":\"DEBUG\"")); + assertTrue(result.contains("\"logger\":\"my-logger\"")); + assertTrue(result.contains("\"msg\":\"This is some very important log message\"")); + assertTrue(result.contains("\"thread\":\"thread-1\"")); + + } + +} diff --git a/pom.xml b/pom.xml index f3425f8..09e1ba3 100644 --- a/pom.xml +++ b/pom.xml @@ -147,6 +147,7 @@ 4.4.0 2.18.2 4.5.14 + 3.4.5