diff --git a/sentry-logback/src/main/java/io/sentry/logback/SentryAppender.java b/sentry-logback/src/main/java/io/sentry/logback/SentryAppender.java index 7c9f6d6cd59..30d2423b39b 100644 --- a/sentry-logback/src/main/java/io/sentry/logback/SentryAppender.java +++ b/sentry-logback/src/main/java/io/sentry/logback/SentryAppender.java @@ -5,6 +5,7 @@ import ch.qos.logback.classic.spi.IThrowableProxy; import ch.qos.logback.classic.spi.StackTraceElementProxy; import ch.qos.logback.core.UnsynchronizedAppenderBase; +import ch.qos.logback.core.encoder.Encoder; import ch.qos.logback.core.filter.Filter; import ch.qos.logback.core.spi.FilterReply; import io.sentry.Sentry; @@ -16,6 +17,7 @@ import io.sentry.event.interfaces.SentryException; import io.sentry.event.interfaces.StackTraceInterface; +import java.nio.charset.StandardCharsets; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Date; @@ -38,6 +40,12 @@ public class SentryAppender extends UnsynchronizedAppenderBase { * Name of the {@link Event#extra} property containing the Thread name. */ public static final String THREAD_NAME = "Sentry-Threadname"; + + /** + * Appender encoder. + */ + protected Encoder encoder; + /** * If set, only events with level = minLevel and up will be recorded. * @@ -91,6 +99,7 @@ protected static Event.Level formatLevel(Level level) { @Override protected void append(ILoggingEvent iLoggingEvent) { + // Do not log the event if the current thread is managed by sentry if (isNotLoggable(iLoggingEvent) || SentryEnvironment.isManagingThread()) { return; @@ -122,10 +131,19 @@ private boolean isNotLoggable(ILoggingEvent iLoggingEvent) { * @return EventBuilder containing details provided by the logging system. */ protected EventBuilder createEventBuilder(ILoggingEvent iLoggingEvent) { + final String formattedMessage; + + if (this.encoder != null) { + byte[] byteArray = this.encoder.encode(iLoggingEvent); + formattedMessage = new String(byteArray, StandardCharsets.UTF_8); + } else { + formattedMessage = iLoggingEvent.getFormattedMessage(); + } + EventBuilder eventBuilder = new EventBuilder() .withSdkIntegration("logback") .withTimestamp(new Date(iLoggingEvent.getTimeStamp())) - .withMessage(iLoggingEvent.getFormattedMessage()) + .withMessage(formattedMessage) .withLogger(iLoggingEvent.getLoggerName()) .withLevel(formatLevel(iLoggingEvent.getLevel())) .withExtra(THREAD_NAME, iLoggingEvent.getThreadName()); @@ -144,7 +162,11 @@ protected EventBuilder createEventBuilder(ILoggingEvent iLoggingEvent) { } for (Map.Entry contextEntry : iLoggingEvent.getLoggerContextVO().getPropertyMap().entrySet()) { - eventBuilder.withExtra(contextEntry.getKey(), contextEntry.getValue()); + if (Sentry.getStoredClient().getMdcTags().contains(contextEntry.getKey())) { + eventBuilder.withTag(contextEntry.getKey(), contextEntry.getValue()); + } else { + eventBuilder.withExtra(contextEntry.getKey(), contextEntry.getValue()); + } } for (Map.Entry mdcEntry : iLoggingEvent.getMDCPropertyMap().entrySet()) { @@ -286,6 +308,19 @@ public void stop() { } } + public Encoder getEncoder() { + return encoder; + } + + /** + * Set logback encoder. + * + * @param encoder logback encoder. + */ + public void setEncoder(Encoder encoder) { + this.encoder = encoder; + } + private class DropSentryFilter extends Filter { @Override public FilterReply decide(ILoggingEvent event) { @@ -296,4 +331,5 @@ public FilterReply decide(ILoggingEvent event) { return FilterReply.NEUTRAL; } } + } diff --git a/sentry-logback/src/test/java/io/sentry/logback/SentryAppenderEventBuildingTest.java b/sentry-logback/src/test/java/io/sentry/logback/SentryAppenderEventBuildingTest.java index 02610cc7ec5..60c79efcc80 100644 --- a/sentry-logback/src/test/java/io/sentry/logback/SentryAppenderEventBuildingTest.java +++ b/sentry-logback/src/test/java/io/sentry/logback/SentryAppenderEventBuildingTest.java @@ -1,6 +1,8 @@ package io.sentry.logback; import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.encoder.PatternLayoutEncoder; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.BasicStatusManager; import ch.qos.logback.core.Context; @@ -41,7 +43,7 @@ public class SentryAppenderEventBuildingTest extends BaseTest { @Before public void setUp() throws Exception { mockSentryClient = mock(SentryClient.class); - mockContext = mock(Context.class); + mockContext = mock(LoggerContext.class); Sentry.setStoredClient(mockSentryClient); sentryAppender = new SentryAppender(); @@ -205,6 +207,22 @@ public void testMdcTakesPrecedenceOverContextProperties() throws Exception { assertThat(event.getExtra(), Matchers.hasEntry(mdcKey, mdcValue)); assertNoErrorsInStatusManager(); } + + @Test + public void testGLobalAddedToExtra() throws Exception { + final String extraKey = "10e09b11-546f-4c57-99b2-cf3c627c8737"; + final String extraValue = "5f7a53b1-4354-4120-a368-78a615705540"; + + + sentryAppender.append(new TestLoggingEvent(null, null, Level.INFO, null, null, null, + null, null, null, 0, Collections.singletonMap(extraKey, extraValue))); + + ArgumentCaptor eventBuilderArgumentCaptor = ArgumentCaptor.forClass(EventBuilder.class); + verify(mockSentryClient).sendEvent(eventBuilderArgumentCaptor.capture()); + Event event = eventBuilderArgumentCaptor.getValue().build(); + assertThat(event.getExtra(), Matchers.hasEntry(extraKey, extraValue)); + assertNoErrorsInStatusManager(); + } @Test public void testSourceUsedAsStacktrace() throws Exception { @@ -244,4 +262,47 @@ public void testExtraTagObtainedFromMdc() throws Exception { assertThat(event.getExtra(), Matchers.hasEntry("other_property", "cb9c92a1-0182-4e9c-866f-b06b271cd196")); assertNoErrorsInStatusManager(); } + + @Test + public void testExtraTagObtainedFromGlobal() throws Exception { + Map propertyMap = new HashMap<>(); + propertyMap.put(mockExtraTag, "47008f35-50c8-4e40-94ca-c8c1a3ddb729"); + propertyMap.put("other_property", "cb9c92a1-0182-4e9c-866f-b06b271cd196"); + + when(mockSentryClient.getMdcTags()).thenReturn(extraTags); + + sentryAppender.append(new TestLoggingEvent(null, null, Level.INFO, null, null, null, null, null, + null, 0, propertyMap)); + + ArgumentCaptor eventBuilderArgumentCaptor = ArgumentCaptor.forClass(EventBuilder.class); + verify(mockSentryClient).sendEvent(eventBuilderArgumentCaptor.capture()); + Event event = eventBuilderArgumentCaptor.getValue().build(); + assertThat(event.getTags().entrySet(), hasSize(1)); + assertThat(event.getTags(), hasEntry(mockExtraTag, "47008f35-50c8-4e40-94ca-c8c1a3ddb729")); + assertThat(event.getExtra(), not(hasKey(mockExtraTag))); + assertThat(event.getExtra(), Matchers.hasEntry("other_property", "cb9c92a1-0182-4e9c-866f-b06b271cd196")); + assertNoErrorsInStatusManager(); + } + + @Test + public void testSetEncoder() throws Exception { + when(mockSentryClient.getMdcTags()).thenReturn(extraTags); + + PatternLayoutEncoder encoder = new PatternLayoutEncoder(); + encoder.setPattern("%-5level : %message"); + encoder.setContext(mockContext); + encoder.start(); + sentryAppender.setEncoder(encoder); + sentryAppender.start(); + + String expectedMessage = "some message"; + sentryAppender.append(new TestLoggingEvent(null, null, Level.INFO, expectedMessage, null, null, null, null, + null, 0)); + + ArgumentCaptor eventBuilderArgumentCaptor = ArgumentCaptor.forClass(EventBuilder.class); + verify(mockSentryClient).sendEvent(eventBuilderArgumentCaptor.capture()); + Event event = eventBuilderArgumentCaptor.getValue().build(); + assertThat(event.getMessage(), is("INFO : " + expectedMessage)); + assertNoErrorsInStatusManager(); + } }