diff --git a/src/main/java/net/logstash/logback/composite/loggingevent/LogstashMarkersJsonProvider.java b/src/main/java/net/logstash/logback/composite/loggingevent/LogstashMarkersJsonProvider.java index 17c74b89..159c0038 100644 --- a/src/main/java/net/logstash/logback/composite/loggingevent/LogstashMarkersJsonProvider.java +++ b/src/main/java/net/logstash/logback/composite/loggingevent/LogstashMarkersJsonProvider.java @@ -17,11 +17,13 @@ import java.io.IOException; import java.util.Iterator; +import java.util.List; import net.logstash.logback.composite.AbstractJsonProvider; import net.logstash.logback.composite.JsonProvider; import net.logstash.logback.marker.LogstashMarker; import net.logstash.logback.marker.Markers; +import net.logstash.logback.util.LogbackUtils; import ch.qos.logback.classic.spi.ILoggingEvent; import com.fasterxml.jackson.core.JsonGenerator; @@ -33,9 +35,23 @@ */ public class LogstashMarkersJsonProvider extends AbstractJsonProvider { + @SuppressWarnings("deprecation") @Override public void writeTo(JsonGenerator generator, ILoggingEvent event) throws IOException { - writeLogstashMarkerIfNecessary(generator, event.getMarker()); + if (LogbackUtils.isVersion13()) { + writeLogstashMarkerIfNecessary(generator, event.getMarkerList()); + } + else { + writeLogstashMarkerIfNecessary(generator, event.getMarker()); + } + } + + private void writeLogstashMarkerIfNecessary(JsonGenerator generator, List markers) throws IOException { + if (markers != null) { + for (Marker marker: markers) { + writeLogstashMarkerIfNecessary(generator, marker); + } + } } private void writeLogstashMarkerIfNecessary(JsonGenerator generator, Marker marker) throws IOException { diff --git a/src/main/java/net/logstash/logback/composite/loggingevent/TagsJsonProvider.java b/src/main/java/net/logstash/logback/composite/loggingevent/TagsJsonProvider.java index 6f18692e..69bff570 100644 --- a/src/main/java/net/logstash/logback/composite/loggingevent/TagsJsonProvider.java +++ b/src/main/java/net/logstash/logback/composite/loggingevent/TagsJsonProvider.java @@ -17,11 +17,13 @@ import java.io.IOException; import java.util.Iterator; +import java.util.List; import net.logstash.logback.composite.AbstractFieldJsonProvider; import net.logstash.logback.composite.FieldNamesAware; import net.logstash.logback.fieldnames.LogstashFieldNames; import net.logstash.logback.marker.LogstashMarker; +import net.logstash.logback.util.LogbackUtils; import ch.qos.logback.classic.spi.ILoggingEvent; import com.fasterxml.jackson.core.JsonGenerator; @@ -41,6 +43,7 @@ public TagsJsonProvider() { setFieldName(FIELD_TAGS); } + @SuppressWarnings("deprecation") @Override public void writeTo(JsonGenerator generator, ILoggingEvent event) throws IOException { /* @@ -48,33 +51,44 @@ public void writeTo(JsonGenerator generator, ILoggingEvent event) throws IOExcep */ boolean hasWrittenStart = false; - final Marker marker = event.getMarker(); - - if (marker != null) { - hasWrittenStart = writeTagIfNecessary(generator, hasWrittenStart, marker); + if (LogbackUtils.isVersion13()) { + hasWrittenStart = writeTagIfNecessary(generator, hasWrittenStart, event.getMarkerList()); + } + else { + hasWrittenStart = writeTagIfNecessary(generator, hasWrittenStart, event.getMarker()); } if (hasWrittenStart) { generator.writeEndArray(); } } - - @SuppressWarnings("deprecation") - private boolean writeTagIfNecessary(JsonGenerator generator, boolean hasWrittenStart, final Marker marker) throws IOException { - if (!LogstashMarkersJsonProvider.isLogstashMarker(marker)) { - if (!hasWrittenStart) { - generator.writeArrayFieldStart(getFieldName()); - hasWrittenStart = true; + private boolean writeTagIfNecessary(JsonGenerator generator, boolean hasWrittenStart, final List markers) throws IOException { + if (markers != null) { + for (Marker marker: markers) { + hasWrittenStart |= writeTagIfNecessary(generator, hasWrittenStart, marker); } - generator.writeString(marker.getName()); } - if (marker.hasReferences()) { - - for (Iterator i = marker.iterator(); i.hasNext();) { - Marker next = (Marker) i.next(); - - hasWrittenStart |= writeTagIfNecessary(generator, hasWrittenStart, next); + + return hasWrittenStart; + } + + private boolean writeTagIfNecessary(JsonGenerator generator, boolean hasWrittenStart, final Marker marker) throws IOException { + if (marker != null) { + if (!LogstashMarkersJsonProvider.isLogstashMarker(marker)) { + if (!hasWrittenStart) { + generator.writeArrayFieldStart(getFieldName()); + hasWrittenStart = true; + } + generator.writeString(marker.getName()); + } + + if (marker.hasReferences()) { + for (Iterator i = marker.iterator(); i.hasNext();) { + Marker next = (Marker) i.next(); + + hasWrittenStart |= writeTagIfNecessary(generator, hasWrittenStart, next); + } } } return hasWrittenStart; diff --git a/src/test/java/net/logstash/logback/composite/loggingevent/LogstashMarkersJsonProviderTest.java b/src/test/java/net/logstash/logback/composite/loggingevent/LogstashMarkersJsonProviderTest.java index 23606404..d3222c7d 100644 --- a/src/test/java/net/logstash/logback/composite/loggingevent/LogstashMarkersJsonProviderTest.java +++ b/src/test/java/net/logstash/logback/composite/loggingevent/LogstashMarkersJsonProviderTest.java @@ -15,51 +15,135 @@ */ package net.logstash.logback.composite.loggingevent; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.io.IOException; -import java.util.Collections; import net.logstash.logback.marker.LogstashMarker; +import net.logstash.logback.util.LogbackUtils; -import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.LoggingEvent; import com.fasterxml.jackson.core.JsonGenerator; +import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.slf4j.Marker; +import org.slf4j.MarkerFactory; @ExtendWith(MockitoExtension.class) public class LogstashMarkersJsonProviderTest { - private LogstashMarkersJsonProvider provider = new LogstashMarkersJsonProvider(); - @Mock private JsonGenerator generator; - @Mock - private ILoggingEvent event; + private LogstashMarkersJsonProvider provider = new LogstashMarkersJsonProvider(); + private int markerCount; - @Mock - private LogstashMarker outerMarker; + + + @Test + public void noMarkers() throws IOException { + LoggingEvent event = createEvent(); + assertThatCode(() -> provider.writeTo(generator, event)).doesNotThrowAnyException(); + } + + + /* + * + */ + @Test + public void singleMarker() throws IOException { + + // event: + // * basic1 -> marker1 -> marker11 + // -> basic12 -> marker121 + // -> marker2 + + + Marker basic1 = createBasicMarker(); + LogstashMarker marker1 = createLogstashMarker(); basic1.add(marker1); + LogstashMarker marker11 = createLogstashMarker(); marker1.add(marker11); + Marker basic12 = createBasicMarker(); marker11.add(basic12); + LogstashMarker marker121 = createLogstashMarker(); basic12.add(marker121); + + LoggingEvent event = createEvent(basic1); + + provider.writeTo(generator, event); + + verify(marker1).writeTo(generator); + verify(marker11).writeTo(generator); + verify(marker121).writeTo(generator); + } - @Mock - private LogstashMarker innerMarker; @Test - public void test() throws IOException { + public void multipleMarkers() throws IOException { + Assumptions.assumeTrue(LogbackUtils::isVersion13); - when(outerMarker.hasReferences()).thenReturn(true); - when(outerMarker.iterator()).thenReturn(Collections.singleton(innerMarker).iterator()); + // event: + // * basic1 -> marker1 + // * marker2 - when(event.getMarker()).thenReturn(outerMarker); + + Marker basic1 = createBasicMarker(); + LogstashMarker marker1 = createLogstashMarker(); basic1.add(marker1); + LogstashMarker marker2 = createLogstashMarker(); + + LoggingEvent event = createEvent(basic1, marker2); provider.writeTo(generator, event); - verify(outerMarker).writeTo(generator); - verify(innerMarker).writeTo(generator); + verify(marker1).writeTo(generator); + verify(marker2).writeTo(generator); + } + + + + // -- Utility methods ------------------------------------------------------------------------- + + private Marker createBasicMarker() { + return MarkerFactory.getDetachedMarker(Integer.toString(this.markerCount++)); } + + private LogstashMarker createLogstashMarker() { + return spy(new TestLogstashMarker(Integer.toString(this.markerCount++))); + } + + @SuppressWarnings("deprecation") + private LoggingEvent createEvent(Marker...markers) { + LoggingEvent event = spy(new LoggingEvent()); + + if (markers != null && markers.length > 0) { + if (LogbackUtils.isVersion13()) { + for (Marker marker: markers) { + event.addMarker(marker); + } + } + else { + if (markers.length > 1) { + throw new IllegalStateException("Logback 1.2 supports only one Marker per event"); + } + when(event.getMarker()).thenReturn(markers[0]); + } + } + + return event; + } + + @SuppressWarnings("serial") + private static class TestLogstashMarker extends LogstashMarker { + TestLogstashMarker(String name) { + super(name); + } + @Override + public void writeTo(JsonGenerator generator) throws IOException { + // noop + } + } } diff --git a/src/test/java/net/logstash/logback/composite/loggingevent/TagsJsonProviderTest.java b/src/test/java/net/logstash/logback/composite/loggingevent/TagsJsonProviderTest.java index ff6747bd..c4c9aef1 100644 --- a/src/test/java/net/logstash/logback/composite/loggingevent/TagsJsonProviderTest.java +++ b/src/test/java/net/logstash/logback/composite/loggingevent/TagsJsonProviderTest.java @@ -16,23 +16,27 @@ package net.logstash.logback.composite.loggingevent; import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import java.io.IOException; -import java.util.Collections; import net.logstash.logback.fieldnames.LogstashFieldNames; import net.logstash.logback.marker.LogstashMarker; +import net.logstash.logback.util.LogbackUtils; import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.LoggingEvent; import com.fasterxml.jackson.core.JsonGenerator; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InOrder; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.slf4j.Marker; +import org.slf4j.MarkerFactory; @ExtendWith(MockitoExtension.class) public class TagsJsonProviderTest { @@ -42,31 +46,27 @@ public class TagsJsonProviderTest { @Mock private JsonGenerator generator; - @Mock private ILoggingEvent event; - - @Mock private Marker marker1; - - @Mock private LogstashMarker marker2; - - @Mock private Marker marker3; + private Marker marker4; + @BeforeEach public void setup() { - when(marker1.hasReferences()).thenReturn(true); - when(marker1.iterator()).thenReturn(Collections.singleton(marker2).iterator()); - - when(marker2.hasReferences()).thenReturn(true); - when(marker2.iterator()).thenReturn(Collections.singleton(marker3).iterator()); - - when(marker1.getName()).thenReturn("marker1"); - when(marker3.getName()).thenReturn("marker3"); + marker1 = createBasicMarker("marker1"); + marker2 = createLogstashMarker("marker2"); + marker3 = createBasicMarker("marker3"); - when(event.getMarker()).thenReturn(marker1); + marker1.add(marker2); + marker2.add(marker3); + marker4 = createBasicMarker("marker4"); + + // Logback 1.3: both markers are added to the event + // Logback 1.2: only marker1 is added to the event + event = createEvent(marker1, marker4); } @Test @@ -79,7 +79,12 @@ public void testDefaultName() throws IOException { inOrder.verify(generator).writeArrayFieldStart(TagsJsonProvider.FIELD_TAGS); inOrder.verify(generator).writeString("marker1"); inOrder.verify(generator).writeString("marker3"); + if (LogbackUtils.isVersion13()) { + inOrder.verify(generator).writeString("marker4"); + } inOrder.verify(generator).writeEndArray(); + + Mockito.verifyNoMoreInteractions(generator); } @Test @@ -93,7 +98,12 @@ public void testFieldName() throws IOException { inOrder.verify(generator).writeArrayFieldStart("newFieldName"); inOrder.verify(generator).writeString("marker1"); inOrder.verify(generator).writeString("marker3"); + if (LogbackUtils.isVersion13()) { + inOrder.verify(generator).writeString("marker4"); + } inOrder.verify(generator).writeEndArray(); + + Mockito.verifyNoMoreInteractions(generator); } @Test @@ -109,7 +119,52 @@ public void testFieldNames() throws IOException { inOrder.verify(generator).writeArrayFieldStart("newFieldName"); inOrder.verify(generator).writeString("marker1"); inOrder.verify(generator).writeString("marker3"); + if (LogbackUtils.isVersion13()) { + inOrder.verify(generator).writeString("marker4"); + } inOrder.verify(generator).writeEndArray(); + + Mockito.verifyNoMoreInteractions(generator); } + + // -- Utility methods ------------------------------------------------------------------------- + + private Marker createBasicMarker(String name) { + return MarkerFactory.getDetachedMarker(name); + } + + private LogstashMarker createLogstashMarker(String name) { + return spy(new TestLogstashMarker(name)); + } + + @SuppressWarnings("deprecation") + private LoggingEvent createEvent(Marker...markers) { + LoggingEvent event = spy(new LoggingEvent()); + + if (markers != null && markers.length > 0) { + if (LogbackUtils.isVersion13()) { + for (Marker marker: markers) { + event.addMarker(marker); + } + } + else { + when(event.getMarker()).thenReturn(markers[0]); + } + } + + return event; + } + + @SuppressWarnings("serial") + private static class TestLogstashMarker extends LogstashMarker { + TestLogstashMarker(String name) { + super(name); + } + + @Override + public void writeTo(JsonGenerator generator) throws IOException { + // noop + } + } } diff --git a/src/test/java/net/logstash/logback/encoder/LogstashEncoderTest.java b/src/test/java/net/logstash/logback/encoder/LogstashEncoderTest.java index a91dd128..a200c5dd 100644 --- a/src/test/java/net/logstash/logback/encoder/LogstashEncoderTest.java +++ b/src/test/java/net/logstash/logback/encoder/LogstashEncoderTest.java @@ -20,6 +20,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import java.io.File; @@ -44,7 +45,7 @@ import ch.qos.logback.classic.Level; import ch.qos.logback.classic.pattern.TargetLengthBasedClassNameAbbreviator; import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.classic.spi.IThrowableProxy; +import ch.qos.logback.classic.spi.LoggingEvent; import ch.qos.logback.classic.spi.ThrowableProxy; import ch.qos.logback.classic.spi.ThrowableProxyUtil; import ch.qos.logback.core.Context; @@ -63,41 +64,40 @@ import org.slf4j.MarkerFactory; public class LogstashEncoderTest { - + private static final Logger LOG = LoggerFactory.getLogger(LogstashEncoderTest.class); private static final JsonFactory FACTORY = new MappingJsonFactory().enable(JsonGenerator.Feature.ESCAPE_NON_ASCII); private static final ObjectMapper MAPPER = new ObjectMapper(FACTORY); private final LogstashEncoder encoder = new LogstashEncoder(); - + private Instant now; - + @BeforeEach public void setup() { // Logback as nanos precision since version 1.3 if (LogbackUtils.isVersion13()) { now = Instant.now(); - } - else { + } else { now = Instant.ofEpochMilli(System.currentTimeMillis()); } } - - + @Test public void basicsAreIncluded_logback12() throws Exception { ILoggingEvent event = mockBasicILoggingEvent(Level.ERROR); - + encoder.start(); byte[] encoded = encoder.encode(event); - + JsonNode node = MAPPER.readTree(encoded); - + verifyBasics(now, node); } protected void verifyBasics(Instant timestamp, JsonNode node) { - assertThat(node.get("@timestamp").textValue()).isEqualTo(DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(TimeZone.getDefault().toZoneId()).format(timestamp)); + assertThat(node.get("@timestamp").textValue()).isEqualTo( + DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(TimeZone.getDefault().toZoneId()).format(timestamp)); assertThat(node.get("@version").textValue()).isEqualTo("1"); assertThat(node.get("logger_name").textValue()).isEqualTo("LoggerName"); assertThat(node.get("thread_name").textValue()).isEqualTo("ThreadName"); @@ -105,18 +105,19 @@ protected void verifyBasics(Instant timestamp, JsonNode node) { assertThat(node.get("level").textValue()).isEqualTo("ERROR"); assertThat(node.get("level_value").intValue()).isEqualTo(40000); } - + @Test public void basicsAreIncludedWithShortenedNames() throws Exception { ILoggingEvent event = mockBasicILoggingEvent(Level.ERROR); - + encoder.setFieldNames(new ShortenedFieldNames()); encoder.start(); byte[] encoded = encoder.encode(event); - + JsonNode node = MAPPER.readTree(encoded); - assertThat(node.get("@timestamp").textValue()).isEqualTo(DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(TimeZone.getDefault().toZoneId()).format(now)); + assertThat(node.get("@timestamp").textValue()).isEqualTo( + DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(TimeZone.getDefault().toZoneId()).format(now)); assertThat(node.get("@version").textValue()).isEqualTo("1"); assertThat(node.get("logger").textValue()).isEqualTo("LoggerName"); assertThat(node.get("thread").textValue()).isEqualTo("ThreadName"); @@ -129,58 +130,54 @@ public void basicsAreIncludedWithShortenedNames() throws Exception { public void customDecorators() { encoder.stop(); encoder.setJsonFactoryDecorator(new JsonFactoryDecorator() { - + @Override public JsonFactory decorate(JsonFactory factory) { return factory.disable(JsonGenerator.Feature.QUOTE_FIELD_NAMES); } }); - + encoder.setJsonGeneratorDecorator(new JsonGeneratorDecorator() { - + @Override public JsonGenerator decorate(JsonGenerator generator) { return generator.useDefaultPrettyPrinter(); } }); - + encoder.start(); - + ILoggingEvent event = mockBasicILoggingEvent(Level.ERROR); - + byte[] encoded = encoder.encode(event); - + String output = new String(encoded, StandardCharsets.UTF_8); - - assertThat(output).isEqualTo(String.format( - "{%n" - + " @timestamp : \"" + DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(TimeZone.getDefault().toZoneId()).format(now) + "\",%n" - + " @version : \"1\",%n" - + " message : \"My message\",%n" - + " logger_name : \"LoggerName\",%n" - + " thread_name : \"ThreadName\",%n" - + " level : \"ERROR\",%n" - + " level_value : 40000%n" - + "}%n")); + + assertThat(output).isEqualTo(String.format("{%n" + " @timestamp : \"" + + DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(TimeZone.getDefault().toZoneId()).format(now) + + "\",%n" + " @version : \"1\",%n" + " message : \"My message\",%n" + + " logger_name : \"LoggerName\",%n" + " thread_name : \"ThreadName\",%n" + " level : \"ERROR\",%n" + + " level_value : 40000%n" + "}%n")); } - @Test public void loggerNameIsShortenedProperly() throws Exception { final int length = 36; - final String shortenedLoggerName = new TargetLengthBasedClassNameAbbreviator(length).abbreviate(DateTimeFormatter.class.getCanonicalName()); + final String shortenedLoggerName = new TargetLengthBasedClassNameAbbreviator(length) + .abbreviate(DateTimeFormatter.class.getCanonicalName()); - ILoggingEvent event = mockBasicILoggingEvent(Level.ERROR); - when(event.getLoggerName()).thenReturn(DateTimeFormatter.class.getCanonicalName()); + LoggingEvent event = mockBasicILoggingEvent(Level.ERROR); + event.setLoggerName(DateTimeFormatter.class.getCanonicalName()); encoder.setFieldNames(new ShortenedFieldNames()); encoder.setShortenedLoggerNameLength(length); encoder.start(); byte[] encoded = encoder.encode(event); - + JsonNode node = MAPPER.readTree(encoded); - assertThat(node.get("@timestamp").textValue()).isEqualTo(DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(TimeZone.getDefault().toZoneId()).format(now)); + assertThat(node.get("@timestamp").textValue()).isEqualTo( + DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(TimeZone.getDefault().toZoneId()).format(now)); assertThat(node.get("@version").textValue()).isEqualTo("1"); assertThat(node.get("logger").textValue()).isEqualTo(shortenedLoggerName); assertThat(node.get("thread").textValue()).isEqualTo("ThreadName"); @@ -188,293 +185,303 @@ public void loggerNameIsShortenedProperly() throws Exception { assertThat(node.get("level").textValue()).isEqualTo("ERROR"); assertThat(node.get("levelVal").intValue()).isEqualTo(40000); } - + @Test public void includingThrowableProxyIncludesStackTrace() throws Exception { - IThrowableProxy throwableProxy = new ThrowableProxy(new Exception("My goodness")); - - ILoggingEvent event = mockBasicILoggingEvent(Level.ERROR); - when(event.getThrowableProxy()).thenReturn(throwableProxy); - + ThrowableProxy throwableProxy = new ThrowableProxy(new Exception("My goodness")); + + LoggingEvent event = mockBasicILoggingEvent(Level.ERROR); + event.setThrowableProxy(throwableProxy); + encoder.start(); byte[] encoded = encoder.encode(event); - + JsonNode node = MAPPER.readTree(encoded); - + assertThat(node.get("stack_trace").textValue()).isEqualTo(ThrowableProxyUtil.asString(throwableProxy)); } - + @Test public void mdcAllIncluded() throws Exception { Map mdcMap = new HashMap<>(); mdcMap.put("thing_one", "One"); mdcMap.put("thing_two", "Three"); - - ILoggingEvent event = mockBasicILoggingEvent(Level.ERROR); - when(event.getMDCPropertyMap()).thenReturn(mdcMap); - + + LoggingEvent event = mockBasicILoggingEvent(Level.ERROR); + event.setMDCPropertyMap(mdcMap); + encoder.start(); byte[] encoded = encoder.encode(event); - + JsonNode node = MAPPER.readTree(encoded); - + assertThat(node.get("thing_one").textValue()).isEqualTo("One"); assertThat(node.get("thing_two").textValue()).isEqualTo("Three"); } - + @Test public void mdcSomeIncluded() throws Exception { Map mdcMap = new HashMap<>(); mdcMap.put("thing_one", "One"); mdcMap.put("thing_two", "Three"); - - ILoggingEvent event = mockBasicILoggingEvent(Level.ERROR); - when(event.getMDCPropertyMap()).thenReturn(mdcMap); - + + LoggingEvent event = mockBasicILoggingEvent(Level.ERROR); + event.setMDCPropertyMap(mdcMap); + encoder.addIncludeMdcKeyName("thing_one"); - + encoder.start(); byte[] encoded = encoder.encode(event); - + JsonNode node = MAPPER.readTree(encoded); - + assertThat(node.get("thing_one").textValue()).isEqualTo("One"); assertThat(node.get("thing_two")).isNull(); } - + @Test public void mdcSomeExcluded() throws Exception { Map mdcMap = new HashMap<>(); mdcMap.put("thing_one", "One"); mdcMap.put("thing_two", "Three"); - - ILoggingEvent event = mockBasicILoggingEvent(Level.ERROR); - when(event.getMDCPropertyMap()).thenReturn(mdcMap); - + + LoggingEvent event = mockBasicILoggingEvent(Level.ERROR); + event.setMDCPropertyMap(mdcMap); + encoder.addExcludeMdcKeyName("thing_two"); - + encoder.start(); byte[] encoded = encoder.encode(event); - + JsonNode node = MAPPER.readTree(encoded); - + assertThat(node.get("thing_one").textValue()).isEqualTo("One"); assertThat(node.get("thing_two")).isNull(); } - + @Test public void mdcNoneIncluded() throws Exception { Map mdcMap = new HashMap<>(); mdcMap.put("thing_one", "One"); mdcMap.put("thing_two", "Three"); - - ILoggingEvent event = mockBasicILoggingEvent(Level.ERROR); + + LoggingEvent event = mockBasicILoggingEvent(Level.ERROR); + event.setMDCPropertyMap(mdcMap); encoder.setIncludeMdc(false); encoder.start(); byte[] encoded = encoder.encode(event); - + JsonNode node = MAPPER.readTree(encoded); - + assertThat(node.get("thing_one")).isNull(); assertThat(node.get("thing_two")).isNull(); } - + @Test public void propertiesInMDCAreIncludedInSubObject() throws Exception { Map mdcMap = new HashMap<>(); mdcMap.put("thing_one", "One"); mdcMap.put("thing_two", "Three"); - - ILoggingEvent event = mockBasicILoggingEvent(Level.ERROR); - when(event.getMDCPropertyMap()).thenReturn(mdcMap); - + + LoggingEvent event = mockBasicILoggingEvent(Level.ERROR); + event.setMDCPropertyMap(mdcMap); + encoder.getFieldNames().setMdc("mdc"); encoder.start(); byte[] encoded = encoder.encode(event); - + JsonNode node = MAPPER.readTree(encoded); - + assertThat(node.get("mdc").get("thing_one").textValue()).isEqualTo("One"); assertThat(node.get("mdc").get("thing_two").textValue()).isEqualTo("Three"); } - + @Test public void nullMDCDoesNotCauseEverythingToBlowUp() { - ILoggingEvent event = mockBasicILoggingEvent(Level.ERROR); - when(event.getMDCPropertyMap()).thenReturn(null); - + LoggingEvent event = mockBasicILoggingEvent(Level.ERROR); + event.setMDCPropertyMap(null); + encoder.start(); assertThatCode(() -> encoder.encode(event)).doesNotThrowAnyException(); } - + @Test public void callerDataIsIncluded() throws Exception { - ILoggingEvent event = mockBasicILoggingEvent(Level.ERROR); - when(event.getMDCPropertyMap()).thenReturn(Collections.emptyMap()); - final StackTraceElement[] stackTraceElements = {new StackTraceElement("caller_class", "method_name", "file_name", 12345)}; - when(event.getCallerData()).thenReturn(stackTraceElements); - + LoggingEvent event = mockBasicILoggingEvent(Level.ERROR); + event.setMDCPropertyMap(Collections.emptyMap()); + StackTraceElement[] stackTraceElements = { + new StackTraceElement("caller_class", "method_name", "file_name", 12345) }; + event.setCallerData(stackTraceElements); + encoder.setIncludeCallerData(true); - + encoder.start(); byte[] encoded = encoder.encode(event); - + JsonNode node = MAPPER.readTree(encoded); - + assertThat(node.get("caller_class_name").textValue()).isEqualTo(stackTraceElements[0].getClassName()); assertThat(node.get("caller_method_name").textValue()).isEqualTo(stackTraceElements[0].getMethodName()); assertThat(node.get("caller_file_name").textValue()).isEqualTo(stackTraceElements[0].getFileName()); assertThat(node.get("caller_line_number").intValue()).isEqualTo(stackTraceElements[0].getLineNumber()); } - + @Test public void callerDataIsIncludedInSubObject() throws Exception { - ILoggingEvent event = mockBasicILoggingEvent(Level.ERROR); - when(event.getMDCPropertyMap()).thenReturn(Collections.emptyMap()); - final StackTraceElement[] stackTraceElements = {new StackTraceElement("caller_class", "method_name", "file_name", 12345)}; - when(event.getCallerData()).thenReturn(stackTraceElements); - + LoggingEvent event = mockBasicILoggingEvent(Level.ERROR); + event.setMDCPropertyMap(Collections.emptyMap()); + StackTraceElement[] stackTraceElements = { + new StackTraceElement("caller_class", "method_name", "file_name", 12345) }; + event.setCallerData(stackTraceElements); + encoder.setIncludeCallerData(true); encoder.getFieldNames().setCaller("caller"); encoder.start(); byte[] encoded = encoder.encode(event); - + JsonNode node = MAPPER.readTree(encoded); - - assertThat(node.get("caller").get("caller_class_name").textValue()).isEqualTo(stackTraceElements[0].getClassName()); - assertThat(node.get("caller").get("caller_method_name").textValue()).isEqualTo(stackTraceElements[0].getMethodName()); - assertThat(node.get("caller").get("caller_file_name").textValue()).isEqualTo(stackTraceElements[0].getFileName()); - assertThat(node.get("caller").get("caller_line_number").intValue()).isEqualTo(stackTraceElements[0].getLineNumber()); + + assertThat(node.get("caller").get("caller_class_name").textValue()) + .isEqualTo(stackTraceElements[0].getClassName()); + assertThat(node.get("caller").get("caller_method_name").textValue()) + .isEqualTo(stackTraceElements[0].getMethodName()); + assertThat(node.get("caller").get("caller_file_name").textValue()) + .isEqualTo(stackTraceElements[0].getFileName()); + assertThat(node.get("caller").get("caller_line_number").intValue()) + .isEqualTo(stackTraceElements[0].getLineNumber()); } - + @Test public void callerDataIsNotIncludedIfSwitchedOff() throws Exception { - ILoggingEvent event = mockBasicILoggingEvent(Level.ERROR); - when(event.getMDCPropertyMap()).thenReturn(Collections.emptyMap()); - + LoggingEvent event = mockBasicILoggingEvent(Level.ERROR); + event.setMDCPropertyMap(Collections.emptyMap()); + StackTraceElement[] stackTraceElements = { + new StackTraceElement("caller_class", "method_name", "file_name", 12345) }; + event.setCallerData(stackTraceElements); + encoder.setIncludeCallerData(false); encoder.start(); byte[] encoded = encoder.encode(event); - + JsonNode node = MAPPER.readTree(encoded); assertThat(node.get("caller_class_name")).isNull(); assertThat(node.get("caller_method_name")).isNull(); assertThat(node.get("caller_file_name")).isNull(); assertThat(node.get("caller_line_number")).isNull(); } - + @Test public void propertiesInContextAreIncluded() throws Exception { Map propertyMap = new HashMap<>(); propertyMap.put("thing_one", "One"); propertyMap.put("thing_two", "Three"); - - final Context context = mock(Context.class); + + Context context = mock(Context.class); when(context.getCopyOfPropertyMap()).thenReturn(propertyMap); - + ILoggingEvent event = mockBasicILoggingEvent(Level.ERROR); - + encoder.setContext(context); encoder.start(); byte[] encoded = encoder.encode(event); - + JsonNode node = MAPPER.readTree(encoded); - + assertThat(node.get("thing_one").textValue()).isEqualTo("One"); assertThat(node.get("thing_two").textValue()).isEqualTo("Three"); } - + @Test public void propertiesInContextAreNotIncludedIfSwitchedOff() throws Exception { Map propertyMap = new HashMap<>(); propertyMap.put("thing_one", "One"); propertyMap.put("thing_two", "Three"); - - final Context context = mock(Context.class); + + Context context = mock(Context.class); + when(context.getCopyOfPropertyMap()).thenReturn(propertyMap); ILoggingEvent event = mockBasicILoggingEvent(Level.ERROR); encoder.setIncludeContext(false); encoder.setContext(context); encoder.start(); byte[] encoded = encoder.encode(event); - + JsonNode node = MAPPER.readTree(encoded); - + assertThat(node.get("thing_one")).isNull(); assertThat(node.get("thing_two")).isNull(); } - + @Test public void propertiesInContextAreIncludedInSubObject() throws Exception { Map propertyMap = new HashMap<>(); propertyMap.put("thing_one", "One"); propertyMap.put("thing_two", "Three"); - - final Context context = mock(Context.class); + + Context context = mock(Context.class); when(context.getCopyOfPropertyMap()).thenReturn(propertyMap); - + ILoggingEvent event = mockBasicILoggingEvent(Level.ERROR); - + encoder.getFieldNames().setContext("context"); encoder.setContext(context); encoder.start(); byte[] encoded = encoder.encode(event); - + JsonNode node = MAPPER.readTree(encoded); - + assertThat(node.get("context").get("thing_one").textValue()).isEqualTo("One"); assertThat(node.get("context").get("thing_two").textValue()).isEqualTo("Three"); } - + @Test public void markerIncludesItselfAsTag() throws Exception { - Marker marker = MarkerFactory.getMarker("hoosh"); - ILoggingEvent event = mockBasicILoggingEvent(Level.INFO); - when(event.getMarker()).thenReturn(marker); - + Marker marker = MarkerFactory.getDetachedMarker("hoosh"); + LoggingEvent event = mockBasicILoggingEvent(Level.INFO); + addMarker(event, marker); + encoder.start(); byte[] encoded = encoder.encode(event); - + JsonNode node = MAPPER.readTree(encoded); - + assertJsonArray(node.findValue("tags"), "hoosh"); } - + @Test public void markerReferencesAreIncludedAsTags() throws Exception { - Marker marker = MarkerFactory.getMarker("bees"); + Marker marker = MarkerFactory.getDetachedMarker("bees"); marker.add(MarkerFactory.getMarker("knees")); - ILoggingEvent event = mockBasicILoggingEvent(Level.INFO); - when(event.getMarker()).thenReturn(marker); - + LoggingEvent event = mockBasicILoggingEvent(Level.INFO); + addMarker(event, marker); + encoder.start(); byte[] encoded = encoder.encode(event); - + JsonNode node = MAPPER.readTree(encoded); - + assertJsonArray(node.findValue("tags"), "bees", "knees"); } - + @Test public void nullMarkerIsIgnored() throws Exception { ILoggingEvent event = mockBasicILoggingEvent(Level.INFO); - when(event.getMarker()).thenReturn(null); - + encoder.start(); byte[] encoded = encoder.encode(event); - + JsonNode node = MAPPER.readTree(encoded); - + assertThat(node.findValue("tags")).isNull(); } @Test public void markerNoneIncluded() throws Exception { - Marker marker = MarkerFactory.getMarker("bees"); - marker.add(MarkerFactory.getMarker("knees")); - ILoggingEvent event = mockBasicILoggingEvent(Level.INFO); - when(event.getMarker()).thenReturn(marker); + Marker marker = MarkerFactory.getDetachedMarker("bees"); + marker.add(MarkerFactory.getDetachedMarker("knees")); + LoggingEvent event = mockBasicILoggingEvent(Level.INFO); + addMarker(event, marker); encoder.setIncludeTags(false); encoder.start(); @@ -487,47 +494,51 @@ public void markerNoneIncluded() throws Exception { @Test public void testAppendJsonMessage() throws Exception { - Object[] argArray = new Object[] {1, Collections.singletonMap("hello", Collections.singletonMap("hello", "world"))}; + Object[] argArray = new Object[] { + 1, + Collections.singletonMap("hello", Collections.singletonMap("hello", "world")) }; Marker marker = append("json_message", argArray); - ILoggingEvent event = mockBasicILoggingEvent(Level.INFO); - when(event.getMarker()).thenReturn(marker); - when(event.getArgumentArray()).thenReturn(argArray); - + LoggingEvent event = mockBasicILoggingEvent(Level.INFO); + addMarker(event, marker); + event.setArgumentArray(argArray); + encoder.start(); byte[] encoded = encoder.encode(event); - + JsonNode node = MAPPER.readTree(encoded); - + assertThat(MAPPER.convertValue(argArray, JsonNode.class)).isEqualTo(node.get("json_message")); } - + @Test public void includeJsonChunk() throws Exception { String customFields = "{\"appname\":\"damnGodWebservice\",\"roles\":[\"customerorder\", \"auth\"], \"buildinfo\": { \"version\" : \"Version 0.1.0-SNAPSHOT\", \"lastcommit\" : \"75473700d5befa953c45f630c6d9105413c16fe1\"} }"; ILoggingEvent event = mockBasicILoggingEvent(Level.INFO); - + encoder.setCustomFields(customFields); encoder.start(); byte[] encoded = encoder.encode(event); - + JsonNode node = MAPPER.readTree(encoded); - + assertThat(node.get("appname").textValue()).isEqualTo("damnGodWebservice"); assertThat(node.get("roles")).isEqualTo((parse("[\"customerorder\", \"auth\"]"))); - assertThat(node.get("buildinfo")).isEqualTo(parse("{ \"version\" : \"Version 0.1.0-SNAPSHOT\", \"lastcommit\" : \"75473700d5befa953c45f630c6d9105413c16fe1\"}")); + assertThat(node.get("buildinfo")).isEqualTo(parse( + "{ \"version\" : \"Version 0.1.0-SNAPSHOT\", \"lastcommit\" : \"75473700d5befa953c45f630c6d9105413c16fe1\"}")); } - + @Test public void customTimeZone() throws Exception { ILoggingEvent event = mockBasicILoggingEvent(Level.ERROR); - + encoder.setTimeZone("UTC"); encoder.start(); byte[] encoded = encoder.encode(event); - + JsonNode node = MAPPER.readTree(encoded); - assertThat(node.get("@timestamp").textValue()).isEqualTo(DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(TimeZone.getTimeZone("UTC").toZoneId()).format(now)); + assertThat(node.get("@timestamp").textValue()).isEqualTo( + DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(TimeZone.getTimeZone("UTC").toZoneId()).format(now)); assertThat(node.get("@version").textValue()).isEqualTo("1"); assertThat(node.get("logger_name").textValue()).isEqualTo("LoggerName"); assertThat(node.get("thread_name").textValue()).isEqualTo("ThreadName"); @@ -535,33 +546,32 @@ public void customTimeZone() throws Exception { assertThat(node.get("level").textValue()).isEqualTo("ERROR"); assertThat(node.get("level_value").intValue()).isEqualTo(40000); } - + public JsonNode parse(String string) throws IOException { return FACTORY.createParser(string).readValueAsTree(); } - + @Test public void testAppendEntries() throws Exception { - ILoggingEvent event = mockBasicILoggingEvent(Level.INFO); - + LoggingEvent event = mockBasicILoggingEvent(Level.INFO); + Map contextMap = new HashMap<>(); contextMap.put("duration", 1200); contextMap.put("remoteResponse", "OK"); contextMap.put("extra", Collections.singletonMap("extraEntry", "extraValue")); - + Object[] argList = new Object[] { "firstParamThatShouldBeIgnored", - Collections.singletonMap("ignoredMapEntry", "whatever"), - }; - - when(event.getArgumentArray()).thenReturn(argList); - + Collections.singletonMap("ignoredMapEntry", "whatever"), }; + + event.setArgumentArray(argList); + Marker marker = appendEntries(contextMap); - when(event.getMarker()).thenReturn(marker); - + addMarker(event, marker); + encoder.start(); byte[] encoded = encoder.encode(event); - + JsonNode node = MAPPER.readTree(encoded); assertThat(node.get("duration")).isNotNull(); assertThat(node.get("duration").intValue()).isEqualTo(1200); @@ -570,10 +580,10 @@ public void testAppendEntries() throws Exception { assertThat(node.get("extra")).isNotNull(); assertThat(node.get("extra").get("extraEntry")).isNotNull(); assertThat(node.get("extra").get("extraEntry").textValue()).isEqualTo("extraValue"); - + assertThat(node.get("ignoredMapEntry")).as("The second map from the end should be ignored").isNull(); } - + @Test public void testEncoderConfiguration() throws Exception { File tempFile = new File(System.getProperty("java.io.tmpdir"), "test.log"); @@ -589,58 +599,57 @@ public void testEncoderConfiguration() throws Exception { JsonNode node = MAPPER.readTree(lines.get(0).getBytes(StandardCharsets.UTF_8)); /* - * The configuration suppresses the version field, - * make sure it doesn't appear. + * The configuration suppresses the version field, make sure it doesn't appear. */ assertThat(node.get("@version")).isNull(); assertThat(node.get(LogstashCommonFieldNames.IGNORE_FIELD_INDICATOR)).isNull(); - + assertThat(node.get("appname").textValue()).isEqualTo("damnGodWebservice"); assertThat(node.get("appendedName").textValue()).isEqualTo("appendedValue"); assertThat(node.get("myMdcKey")).isNull(); assertThat(node.get("logger").textValue()).isEqualTo(LogstashEncoderTest.class.getName()); assertThat(node.get("roles")).isEqualTo(parse("[\"customerorder\", \"auth\"]")); - assertThat(node.get("buildinfo")).isEqualTo(parse("{ \"version\" : \"Version 0.1.0-SNAPSHOT\", \"lastcommit\" : \"75473700d5befa953c45f630c6d9105413c16fe1\"}")); + assertThat(node.get("buildinfo")).isEqualTo(parse( + "{ \"version\" : \"Version 0.1.0-SNAPSHOT\", \"lastcommit\" : \"75473700d5befa953c45f630c6d9105413c16fe1\"}")); } - + @Test public void testCustomFields() { String customFields = "{\"foo\":\"bar\"}"; encoder.setCustomFields(customFields); assertThat(encoder.getCustomFields()).isEqualTo(customFields); - + } - + @Test public void unixTimestampAsNumber() throws Exception { ILoggingEvent event = mockBasicILoggingEvent(Level.ERROR); - + encoder.setTimestampPattern(AbstractFormattedTimestampJsonProvider.UNIX_TIMESTAMP_AS_NUMBER); encoder.start(); byte[] encoded = encoder.encode(event); - + JsonNode node = MAPPER.readTree(encoded); - + assertThat(node.get("@timestamp").numberValue()).isEqualTo(event.getTimeStamp()); } - + @Test public void unixTimestampAsString() throws Exception { ILoggingEvent event = mockBasicILoggingEvent(Level.ERROR); - + encoder.setTimestampPattern(AbstractFormattedTimestampJsonProvider.UNIX_TIMESTAMP_AS_STRING); encoder.start(); byte[] encoded = encoder.encode(event); - + JsonNode node = MAPPER.readTree(encoded); - + assertThat(node.get("@timestamp").textValue()).isEqualTo(Long.toString(event.getTimeStamp())); } - + @Test public void testMessageSplitEnabled() throws Exception { - ILoggingEvent event = mockBasicILoggingEvent(Level.ERROR); - when(event.getFormattedMessage()).thenReturn(buildMultiLineMessage("###")); + LoggingEvent event = mockBasicILoggingEvent(Level.ERROR, buildMultiLineMessage("###")); encoder.setMessageSplitRegex("#+"); assertThat(encoder.getMessageSplitRegex()).isEqualTo("#+"); @@ -653,8 +662,9 @@ public void testMessageSplitEnabled() throws Exception { @Test public void testMessageSplitDisabled() throws Exception { - ILoggingEvent event = mockBasicILoggingEvent(Level.ERROR); - when(event.getFormattedMessage()).thenReturn(buildMultiLineMessage(System.lineSeparator())); + String message = buildMultiLineMessage(System.lineSeparator()); + + LoggingEvent event = mockBasicILoggingEvent(Level.ERROR, message); encoder.setMessageSplitRegex(null); assertThat(encoder.getMessageSplitRegex()).isNull(); @@ -663,13 +673,14 @@ public void testMessageSplitDisabled() throws Exception { encoder.stop(); assertThat(node.path("message").isTextual()).isTrue(); - assertThat(node.path("message").textValue()).isEqualTo(buildMultiLineMessage(System.lineSeparator())); + assertThat(node.path("message").textValue()).isEqualTo(message); } @Test public void testMessageSplitDisabledByDefault() throws Exception { - ILoggingEvent event = mockBasicILoggingEvent(Level.ERROR); - when(event.getFormattedMessage()).thenReturn(buildMultiLineMessage(System.lineSeparator())); + String message = buildMultiLineMessage(System.lineSeparator()); + + LoggingEvent event = mockBasicILoggingEvent(Level.ERROR, message); assertThat(encoder.getMessageSplitRegex()).isNull(); encoder.start(); @@ -677,7 +688,7 @@ public void testMessageSplitDisabledByDefault() throws Exception { encoder.stop(); assertThat(node.path("message").isTextual()).isTrue(); - assertThat(node.path("message").textValue()).isEqualTo(buildMultiLineMessage(System.lineSeparator())); + assertThat(node.path("message").textValue()).isEqualTo(message); } private void assertJsonArray(JsonNode jsonNode, String... expected) { @@ -690,23 +701,35 @@ private void assertJsonArray(JsonNode jsonNode, String... expected) { } assertThat(values).containsExactly(expected); } - - - private ILoggingEvent mockBasicILoggingEvent(Level level) { - ILoggingEvent event = mock(ILoggingEvent.class); - when(event.getLoggerName()).thenReturn("LoggerName"); - when(event.getThreadName()).thenReturn("ThreadName"); - when(event.getFormattedMessage()).thenReturn("My message"); - when(event.getLevel()).thenReturn(level); - - when(event.getTimeStamp()).thenReturn(now.toEpochMilli()); + + private LoggingEvent mockBasicILoggingEvent(Level level) { + return mockBasicILoggingEvent(level, "My message"); + } + + private LoggingEvent mockBasicILoggingEvent(Level level, String message) { + LoggingEvent event = new LoggingEvent(); + event.setLoggerName("LoggerName"); + event.setThreadName("ThreadName"); + event.setMessage(message); + event.setLevel(level); + if (LogbackUtils.isVersion13()) { - when(event.getInstant()).thenReturn(now); + event.setInstant(now); + } else { + event.setTimeStamp(now.toEpochMilli()); } - - return event; + + return spy(event); } - + + private void addMarker(LoggingEvent event, Marker marker) { + if (LogbackUtils.isVersion13()) { + event.addMarker(marker); + } else { + when(event.getMarker()).thenReturn(marker); + } + } + private static String buildMultiLineMessage(String lineSeparator) { return String.join(lineSeparator, "line1", "line2", "line3"); }