diff --git a/README.md b/README.md index a72e0cb..a9c998a 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,21 @@ Structured logging utility. * Works with log consumers that can digest JSON logs, like the **ELK Stack** or **Datadog**. * Framework agnostic (works well with **Spring**, because Spring Boot uses logback out of the box) +**Table of Contents** + +* [Why use this?](#why-use-this) +* [Example](#example) +* [Changes](#changes) + * [1.0.3](#103) +* [Usage](#usage) + * [Add structured-logging as a dependency](#add-structured-logging-as-a-dependency) + * [Define how Objects should be named in MDC](#define-how-objects-should-be-named-in-mdc) + * [Put Objects into MDC](#put-objects-into-mdc) + * [Excluding properties from serialization](#excluding-properties-from-serialization) + * [Configure Logback for Logstash](#configure-logback-for-logstash) + * [Configure a Task Decorator in Spring](#configure-a-task-decorator-in-spring) + * [Test your logging](#test-your-logging) + ## Why use this? If you use your logs for monitoring, alerting or visualization, you want them to have structured information so you can properly build your monitoring/alerting/visualizations on that. @@ -42,6 +57,19 @@ If there are log messages that happen in the context of an order, you may want t } ``` +## Changes + +### 1.0.3 + +* Added proper serialization for further JSR310 types. Now properly serializes + * Instant (new) + * LocalDate (new) + * LocalDateTime + * OffsetDateTime + * OffsetTime (new) + * Period (new) + * ZonedDateTime (new) + ## Usage To use this, you need to: @@ -62,7 +90,7 @@ If you use maven, add this to your pom.xml: de.dm.infrastructure structured-logging - 1.0.2 + 1.0.3 test ``` diff --git a/pom.xml b/pom.xml index ddef34c..dd5b170 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ de.dm.infrastructure structured-logging - 1.0.3-SNAPSHOT + 1.0.4-SNAPSHOT structured-logging Structured logging and log testing diff --git a/src/main/java/de/dm/prom/structuredlogging/MdcContext.java b/src/main/java/de/dm/prom/structuredlogging/MdcContext.java index 52237d6..aa0b640 100644 --- a/src/main/java/de/dm/prom/structuredlogging/MdcContext.java +++ b/src/main/java/de/dm/prom/structuredlogging/MdcContext.java @@ -7,8 +7,14 @@ import lombok.extern.slf4j.Slf4j; import org.slf4j.MDC; +import java.lang.reflect.InvocationTargetException; +import java.time.Instant; +import java.time.LocalDate; import java.time.LocalDateTime; import java.time.OffsetDateTime; +import java.time.OffsetTime; +import java.time.Period; +import java.time.ZonedDateTime; /** * a context that can be used to wrap MDC information in a try-with-resources block. @@ -56,14 +62,15 @@ private MdcContext(String key, String value) { public void close() { if (oldValue == null) { MDC.remove(key); - } - else { + } else { MDC.put(key, oldValue); } } private static String toJson(Object object) { - String objectToJson = "{\"json_error\":\"Unserializable Object.\"}"; //needs to be an object, not a string, for Kibana. Otherwise, Kibana will throw away the log entry because the field has the wrong type. + String objectToJson = "{\"json_error\":\"Unserializable Object.\"}"; + //needs to be an object, not a string, for Kibana. Otherwise, Kibana will throw away the log entry because the field has the wrong type. + try { objectToJson = getObjectMapper().writeValueAsString(object); } catch (JsonProcessingException e) { @@ -73,10 +80,16 @@ private static String toJson(Object object) { } private static ObjectMapper getObjectMapper() { - ObjectMapper objectMapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); - module.addSerializer(OffsetDateTime.class, new ToStringSerializer()); + module.addSerializer(Instant.class, new ToStringSerializer()); + module.addSerializer(LocalDate.class, new ToStringSerializer()); module.addSerializer(LocalDateTime.class, new ToStringSerializer()); + module.addSerializer(OffsetDateTime.class, new ToStringSerializer()); + module.addSerializer(OffsetTime.class, new ToStringSerializer()); + module.addSerializer(Period.class, new ToStringSerializer()); + module.addSerializer(ZonedDateTime.class, new ToStringSerializer()); + + ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(module); return objectMapper; } @@ -98,8 +111,7 @@ private static void logOverwriting(String key, String value, String oldValue) { if (!oldValue.equals(value)) { log.error("{} The old value differs from new value. This should never happen, because it messes up the MDC context. Old value: {} - new value: {}", message, oldValue, value); - } - else { + } else { log.warn("{} The value is overwritten with the same value. This is superfluous and should be removed.", message); } } diff --git a/src/test/java/de/dm/prom/structuredlogging/ExampleBean.java b/src/test/java/de/dm/prom/structuredlogging/ExampleBean.java index 94f0345..7eeb50b 100644 --- a/src/test/java/de/dm/prom/structuredlogging/ExampleBean.java +++ b/src/test/java/de/dm/prom/structuredlogging/ExampleBean.java @@ -3,10 +3,17 @@ import lombok.Builder; import lombok.Data; +import java.time.Instant; +import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.LocalTime; import java.time.Month; import java.time.OffsetDateTime; +import java.time.OffsetTime; +import java.time.Period; +import java.time.ZoneId; import java.time.ZoneOffset; +import java.time.ZonedDateTime; @Data @Builder @@ -15,6 +22,11 @@ class ExampleBean { private int age; private LocalDateTime importantTime; private OffsetDateTime importantOffsetTime; + private Instant instant; + private LocalDate localDate; + private OffsetTime offsetTime; + private Period period; + private ZonedDateTime zonedDateTime; static ExampleBean getExample() { LocalDateTime importantTime = LocalDateTime.of(2019, Month.JANUARY, 1, 13, 37); @@ -24,6 +36,11 @@ static ExampleBean getExample() { .age(35) .importantTime(importantTime) .importantOffsetTime(OffsetDateTime.of(importantTime, ZoneOffset.of("+01:00"))) + .instant(Instant.ofEpochMilli(1000)) + .localDate(LocalDate.of(2020, 1, 1)) + .offsetTime(OffsetTime.of(LocalTime.of(13, 37), ZoneOffset.of("+01:00"))) + .period(Period.ofDays(42)) + .zonedDateTime(ZonedDateTime.of(importantTime, ZoneId.of("UTC"))) .build(); } } diff --git a/src/test/java/de/dm/prom/structuredlogging/MdcContextUnitTest.java b/src/test/java/de/dm/prom/structuredlogging/MdcContextUnitTest.java index 2a81d34..f0efb76 100644 --- a/src/test/java/de/dm/prom/structuredlogging/MdcContextUnitTest.java +++ b/src/test/java/de/dm/prom/structuredlogging/MdcContextUnitTest.java @@ -16,7 +16,15 @@ @Slf4j public class MdcContextUnitTest { - private static final String SAMPLE_BEAN_JSON = "{\"name\":\"John Doe\",\"age\":35,\"importantTime\":\"2019-01-01T13:37\",\"importantOffsetTime\":\"2019-01-01T13:37+01:00\"}"; + private static final String SAMPLE_BEAN_JSON = "{\"name\":\"John Doe\"," + + "\"age\":35," + + "\"importantTime\":\"2019-01-01T13:37\"," + + "\"importantOffsetTime\":\"2019-01-01T13:37+01:00\"," + + "\"instant\":\"1970-01-01T00:00:01Z\"," + + "\"localDate\":\"2020-01-01\"," + + "\"offsetTime\":\"13:37+01:00\"," + + "\"period\":\"P42D\"," + + "\"zonedDateTime\":\"2019-01-01T13:37Z[UTC]\"}"; @Rule public LogCapture logCapture = LogCapture.forUnitTest(); diff --git a/src/test/java/de/dm/prom/structuredlogging/StructuredMdcJsonProviderUnitTest.java b/src/test/java/de/dm/prom/structuredlogging/StructuredMdcJsonProviderUnitTest.java index 5a9f545..81661bf 100644 --- a/src/test/java/de/dm/prom/structuredlogging/StructuredMdcJsonProviderUnitTest.java +++ b/src/test/java/de/dm/prom/structuredlogging/StructuredMdcJsonProviderUnitTest.java @@ -23,7 +23,24 @@ @Slf4j public class StructuredMdcJsonProviderUnitTest { - private static String SAMPLE_LOGSTASH_JSON_LOG = "{\"@version\":\"1\",\"message\":\"something in which the ExampleBean context is relevant\",\"logger_name\":\"de.dm.prom.structuredlogging.StructuredMdcJsonProviderUnitTest\",\"thread_name\":\"main\",\"level\":\"INFO\",\"level_value\":20000,\"an_unmanaged_mdc_field\":\"some value\",\"example_bean\":{\"name\":\"John Doe\",\"age\":35,\"importantTime\":\"2019-01-01T13:37\",\"importantOffsetTime\":\"2019-01-01T13:37+01:00\"}}"; + private static String SAMPLE_LOGSTASH_JSON_LOG = "{\"@version\":\"1\"," + + "\"message\":\"something in which the ExampleBean context is relevant\"," + + "\"logger_name\":\"de.dm.prom.structuredlogging.StructuredMdcJsonProviderUnitTest\"," + + "\"thread_name\":\"main\"," + + "\"level\":\"INFO\"," + + "\"level_value\":20000," + + "\"an_unmanaged_mdc_field\":\"some value\"," + + "\"example_bean\":" + + "{\"name\":\"John Doe\"," + + "\"age\":35," + + "\"importantTime\":\"2019-01-01T13:37\"," + + "\"importantOffsetTime\":\"2019-01-01T13:37+01:00\"," + + "\"instant\":\"1970-01-01T00:00:01Z\"," + + "\"localDate\":\"2020-01-01\"," + + "\"offsetTime\":\"13:37+01:00\"," + + "\"period\":\"P42D\"," + + "\"zonedDateTime\":\"2019-01-01T13:37Z[UTC]\"}}"; + private Logger rootLogger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); @Rule