diff --git a/sentry-logback/api/sentry-logback.api b/sentry-logback/api/sentry-logback.api index 9b080a6655..5e5ace5cac 100644 --- a/sentry-logback/api/sentry-logback.api +++ b/sentry-logback/api/sentry-logback.api @@ -11,6 +11,7 @@ public class io/sentry/logback/SentryAppender : ch/qos/logback/core/Unsynchroniz protected fun createEvent (Lch/qos/logback/classic/spi/ILoggingEvent;)Lio/sentry/SentryEvent; public fun getMinimumBreadcrumbLevel ()Lch/qos/logback/classic/Level; public fun getMinimumEventLevel ()Lch/qos/logback/classic/Level; + public fun setEncoder (Lch/qos/logback/core/encoder/Encoder;)V public fun setMinimumBreadcrumbLevel (Lch/qos/logback/classic/Level;)V public fun setMinimumEventLevel (Lch/qos/logback/classic/Level;)V public fun setOptions (Lio/sentry/SentryOptions;)V 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 2c8f18e116..dfb7e1fb84 100644 --- a/sentry-logback/src/main/java/io/sentry/logback/SentryAppender.java +++ b/sentry-logback/src/main/java/io/sentry/logback/SentryAppender.java @@ -6,6 +6,7 @@ import ch.qos.logback.classic.Level; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.ThrowableProxy; +import ch.qos.logback.core.encoder.Encoder; import ch.qos.logback.core.UnsynchronizedAppenderBase; import com.jakewharton.nopen.annotation.Open; import io.sentry.Breadcrumb; @@ -20,6 +21,7 @@ import io.sentry.protocol.Message; import io.sentry.protocol.SdkVersion; import io.sentry.util.CollectionUtils; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -39,6 +41,7 @@ public class SentryAppender extends UnsynchronizedAppenderBase { private @Nullable ITransportFactory transportFactory; private @NotNull Level minimumBreadcrumbLevel = Level.INFO; private @NotNull Level minimumEventLevel = Level.ERROR; + private @Nullable Encoder encoder; @Override public void start() { @@ -91,7 +94,7 @@ protected void append(@NotNull ILoggingEvent eventObject) { final SentryEvent event = new SentryEvent(DateUtils.getDateTime(loggingEvent.getTimeStamp())); final Message message = new Message(); message.setMessage(loggingEvent.getMessage()); - message.setFormatted(loggingEvent.getFormattedMessage()); + message.setFormatted(formatted(loggingEvent)); message.setParams(toParams(loggingEvent.getArgumentArray())); event.setMessage(message); event.setLogger(loggingEvent.getLoggerName()); @@ -137,6 +140,19 @@ protected void append(@NotNull ILoggingEvent eventObject) { return event; } + private String formatted(@NotNull ILoggingEvent loggingEvent) { + if (encoder != null) { + try { + return new String(encoder.encode(loggingEvent), StandardCharsets.UTF_8); + } catch (final Throwable t) { + // catch exceptions from possibly incorrectly configured encoder + // and fallback to default formatted message + addWarn("Failed to encode logging event", t); + } + } + return loggingEvent.getFormattedMessage(); + } + private @NotNull List toParams(@Nullable Object[] arguments) { if (arguments != null) { return Arrays.stream(arguments) @@ -158,7 +174,7 @@ protected void append(@NotNull ILoggingEvent eventObject) { final Breadcrumb breadcrumb = new Breadcrumb(); breadcrumb.setLevel(formatLevel(loggingEvent.getLevel())); breadcrumb.setCategory(loggingEvent.getLoggerName()); - breadcrumb.setMessage(loggingEvent.getFormattedMessage()); + breadcrumb.setMessage(formatted(loggingEvent)); return breadcrumb; } @@ -222,4 +238,8 @@ public void setMinimumEventLevel(final @Nullable Level minimumEventLevel) { void setTransportFactory(final @Nullable ITransportFactory transportFactory) { this.transportFactory = transportFactory; } + + public void setEncoder(Encoder encoder) { + this.encoder = encoder; + } } diff --git a/sentry-logback/src/test/kotlin/io/sentry/logback/SentryAppenderTest.kt b/sentry-logback/src/test/kotlin/io/sentry/logback/SentryAppenderTest.kt index 11c2afba98..d809f34fdc 100644 --- a/sentry-logback/src/test/kotlin/io/sentry/logback/SentryAppenderTest.kt +++ b/sentry-logback/src/test/kotlin/io/sentry/logback/SentryAppenderTest.kt @@ -2,6 +2,10 @@ 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.encoder.Encoder +import ch.qos.logback.core.encoder.EncoderBase import ch.qos.logback.core.status.Status import com.nhaarman.mockitokotlin2.any import com.nhaarman.mockitokotlin2.anyOrNull @@ -31,7 +35,7 @@ import kotlin.test.assertNull import kotlin.test.assertTrue class SentryAppenderTest { - private class Fixture(dsn: String? = "http://key@localhost/proj", minimumBreadcrumbLevel: Level? = null, minimumEventLevel: Level? = null, contextTags: List? = null) { + private class Fixture(dsn: String? = "http://key@localhost/proj", minimumBreadcrumbLevel: Level? = null, minimumEventLevel: Level? = null, contextTags: List? = null, encoder: Encoder? = null) { val logger: Logger = LoggerFactory.getLogger(SentryAppenderTest::class.java) val loggerContext = LoggerFactory.getILoggerFactory() as LoggerContext val transportFactory = mock() @@ -49,10 +53,13 @@ class SentryAppenderTest { appender.setMinimumEventLevel(minimumEventLevel) appender.context = loggerContext appender.setTransportFactory(transportFactory) + encoder?.context = loggerContext + appender.setEncoder(encoder) val rootLogger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME) rootLogger.level = Level.TRACE rootLogger.addAppender(appender) appender.start() + encoder?.start() loggerContext.start() } } @@ -88,6 +95,7 @@ class SentryAppenderTest { ) } + @Test fun `converts message`() { fixture = Fixture(minimumEventLevel = Level.DEBUG) @@ -106,6 +114,59 @@ class SentryAppenderTest { ) } + @Test + fun `encodes message`() { + var encoder = PatternLayoutEncoder() + encoder.pattern = "encoderadded %msg" + fixture = Fixture(minimumEventLevel = Level.DEBUG, encoder = encoder) + fixture.logger.info("testing encoding"); + + verify(fixture.transport).send( + checkEvent { event -> + assertNotNull(event.message) { message -> + assertEquals("encoderadded testing encoding", message.formatted) + assertEquals("testing encoding", message.message) + } + assertEquals("io.sentry.logback.SentryAppenderTest", event.logger) + }, + anyOrNull() + ) + } + + class ThrowingEncoder: EncoderBase { + constructor(): super() + override fun headerBytes(): ByteArray { + TODO("Not yet implemented") + } + + override fun footerBytes(): ByteArray { + TODO("Not yet implemented") + } + + override fun encode(event: ILoggingEvent?): ByteArray { + TODO("Not yet implemented") + } + } + + @Test + fun `fallsback when encoder throws`() { + var encoder = ThrowingEncoder() + fixture = Fixture(minimumEventLevel = Level.DEBUG, encoder = encoder) + fixture.logger.info("testing when encoder throws"); + + verify(fixture.transport).send( + checkEvent { event -> + assertNotNull(event.message) { message -> + assertEquals("testing when encoder throws", message.formatted) + assertEquals("testing when encoder throws", message.message) + } + assertEquals("io.sentry.logback.SentryAppenderTest", event.logger) + }, + anyOrNull() + ) + } + + @Test fun `event date is in UTC`() { fixture = Fixture(minimumEventLevel = Level.DEBUG)