Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for using Encoder with logback.SentryAppender #2246

Merged
merged 7 commits into from Oct 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -10,6 +10,7 @@

### Features

- Add support for using Encoder with logback.SentryAppender ([#2246](https://github.com/getsentry/sentry-java/pull/2246))
- Add captureProfile method to hub and client ([#2290](https://github.com/getsentry/sentry-java/pull/2290))

## 6.5.0
Expand Down
1 change: 1 addition & 0 deletions sentry-logback/api/sentry-logback.api
Expand Up @@ -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
Expand Down
Expand Up @@ -7,6 +7,7 @@
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.ThrowableProxy;
import ch.qos.logback.core.UnsynchronizedAppenderBase;
import ch.qos.logback.core.encoder.Encoder;
import com.jakewharton.nopen.annotation.Open;
import io.sentry.Breadcrumb;
import io.sentry.DateUtils;
Expand All @@ -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;
Expand All @@ -39,6 +41,7 @@ public class SentryAppender extends UnsynchronizedAppenderBase<ILoggingEvent> {
private @Nullable ITransportFactory transportFactory;
private @NotNull Level minimumBreadcrumbLevel = Level.INFO;
private @NotNull Level minimumEventLevel = Level.ERROR;
private @Nullable Encoder<ILoggingEvent> encoder;

@Override
public void start() {
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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<String> toParams(@Nullable Object[] arguments) {
if (arguments != null) {
return Arrays.stream(arguments)
Expand All @@ -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;
}

Expand Down Expand Up @@ -222,4 +238,8 @@ public void setMinimumEventLevel(final @Nullable Level minimumEventLevel) {
void setTransportFactory(final @Nullable ITransportFactory transportFactory) {
this.transportFactory = transportFactory;
}

public void setEncoder(Encoder<ILoggingEvent> encoder) {
this.encoder = encoder;
}
}
Expand Up @@ -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
Expand Down Expand Up @@ -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<String>? = null) {
private class Fixture(dsn: String? = "http://key@localhost/proj", minimumBreadcrumbLevel: Level? = null, minimumEventLevel: Level? = null, contextTags: List<String>? = null, encoder: Encoder<ILoggingEvent>? = null) {
val logger: Logger = LoggerFactory.getLogger(SentryAppenderTest::class.java)
val loggerContext = LoggerFactory.getILoggerFactory() as LoggerContext
val transportFactory = mock<ITransportFactory>()
Expand All @@ -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()
}
}
Expand All @@ -61,6 +68,7 @@ class SentryAppenderTest {

@AfterTest
fun `stop logback`() {
fixture.loggerContext.statusManager.clear()
fixture.loggerContext.stop()
Sentry.close()
}
Expand Down Expand Up @@ -106,6 +114,58 @@ 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<ILoggingEvent> {
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)
Expand Down