diff --git a/CHANGELOG.md b/CHANGELOG.md index ec13dc20e5..5a366d5ef7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ - Session Replay: Use main thread looper to schedule replay capture ([#4542](https://github.com/getsentry/sentry-java/pull/4542)) - Use single `LifecycleObserver` and multi-cast it to the integrations interested in lifecycle states ([#4567](https://github.com/getsentry/sentry-java/pull/4567)) +- Add `sentry.origin` attribute to logs ([#4618](https://github.com/getsentry/sentry-java/pull/4618)) + - This helps identify which integration captured a log event - Prewarm `SentryExecutorService` for better performance at runtime ([#4606](https://github.com/getsentry/sentry-java/pull/4606)) ### Fixes diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryLogcatAdapter.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryLogcatAdapter.java index ebf99e2b97..1e649c1783 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryLogcatAdapter.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryLogcatAdapter.java @@ -6,6 +6,7 @@ import io.sentry.Sentry; import io.sentry.SentryLevel; import io.sentry.SentryLogLevel; +import io.sentry.logger.SentryLogParameters; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -56,10 +57,13 @@ private static void addAsLog( return; } final @Nullable String trMessage = tr != null ? tr.getMessage() : null; + final @NotNull SentryLogParameters params = new SentryLogParameters(); + params.setOrigin("auto.log.logcat"); + if (tr == null || trMessage == null) { - scopes.logger().log(level, msg); + scopes.logger().log(level, params, msg); } else { - scopes.logger().log(level, msg != null ? (msg + "\n" + trMessage) : trMessage); + scopes.logger().log(level, params, msg != null ? (msg + "\n" + trMessage) : trMessage); } } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/SentryLogcatAdapterTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/SentryLogcatAdapterTest.kt index 1e58bbf6e7..1a84a1282d 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/SentryLogcatAdapterTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/SentryLogcatAdapterTest.kt @@ -64,6 +64,7 @@ class SentryLogcatAdapterTest { SentryLogcatAdapter.v(tag, "$commonMsg verbose") fixture.breadcrumbs.first().assert(tag, "$commonMsg verbose", SentryLevel.DEBUG) fixture.logs.first().assert("$commonMsg verbose", SentryLogLevel.TRACE) + assertEquals("auto.log.logcat", fixture.logs.first().attributes?.get("sentry.origin")?.value) } @Test diff --git a/sentry-android-timber/src/main/java/io/sentry/android/timber/SentryTimberTree.kt b/sentry-android-timber/src/main/java/io/sentry/android/timber/SentryTimberTree.kt index c0e996bdba..743501d664 100644 --- a/sentry-android-timber/src/main/java/io/sentry/android/timber/SentryTimberTree.kt +++ b/sentry-android-timber/src/main/java/io/sentry/android/timber/SentryTimberTree.kt @@ -6,6 +6,7 @@ import io.sentry.IScopes import io.sentry.SentryEvent import io.sentry.SentryLevel import io.sentry.SentryLogLevel +import io.sentry.logger.SentryLogParameters import io.sentry.protocol.Message import timber.log.Timber @@ -244,12 +245,15 @@ public class SentryTimberTree( ) { // checks the log level if (isLoggable(sentryLogLevel, minLogLevel)) { + val params = SentryLogParameters() + params.origin = "auto.log.timber" + val throwableMsg = throwable?.message when { msg != null && throwableMsg != null -> - scopes.logger().log(sentryLogLevel, "$msg\n$throwableMsg", *args) - msg != null -> scopes.logger().log(sentryLogLevel, msg, *args) - throwableMsg != null -> scopes.logger().log(sentryLogLevel, throwableMsg, *args) + scopes.logger().log(sentryLogLevel, params, "$msg\n$throwableMsg", *args) + msg != null -> scopes.logger().log(sentryLogLevel, params, msg, *args) + throwableMsg != null -> scopes.logger().log(sentryLogLevel, params, throwableMsg, *args) } } } diff --git a/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberTreeTest.kt b/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberTreeTest.kt index e77e33e42d..2e610cf279 100644 --- a/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberTreeTest.kt +++ b/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberTreeTest.kt @@ -5,6 +5,7 @@ import io.sentry.Scopes import io.sentry.SentryLevel import io.sentry.SentryLogLevel import io.sentry.logger.ILoggerApi +import io.sentry.logger.SentryLogParameters import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals @@ -248,21 +249,28 @@ class SentryTimberTreeTest { val sut = fixture.getSut() sut.e("test count: %d %d", 32, 5) - verify(fixture.logs).log(eq(SentryLogLevel.ERROR), eq("test count: %d %d"), eq(32), eq(5)) + verify(fixture.logs) + .log( + eq(SentryLogLevel.ERROR), + check { assertEquals("auto.log.timber", it.origin) }, + eq("test count: %d %d"), + eq(32), + eq(5), + ) } @Test fun `Tree adds a log if min level is equal`() { val sut = fixture.getSut() sut.i(Throwable("test")) - verify(fixture.logs).log(any(), any()) + verify(fixture.logs).log(any(), any(), any()) } @Test fun `Tree adds a log if min level is higher`() { val sut = fixture.getSut() sut.e(Throwable("test")) - verify(fixture.logs).log(any(), any(), any()) + verify(fixture.logs).log(any(), any(), any(), any()) } @Test @@ -277,7 +285,12 @@ class SentryTimberTreeTest { val sut = fixture.getSut() sut.i("message") - verify(fixture.logs).log(eq(SentryLogLevel.INFO), eq("message")) + verify(fixture.logs) + .log( + eq(SentryLogLevel.INFO), + check { assertEquals("auto.log.timber", it.origin) }, + eq("message"), + ) } @Test @@ -285,7 +298,12 @@ class SentryTimberTreeTest { val sut = fixture.getSut() sut.e(Throwable("test")) - verify(fixture.logs).log(eq(SentryLogLevel.ERROR), eq("test")) + verify(fixture.logs) + .log( + eq(SentryLogLevel.ERROR), + check { assertEquals("auto.log.timber", it.origin) }, + eq("test"), + ) } @Test @@ -300,7 +318,12 @@ class SentryTimberTreeTest { val sut = fixture.getSut() sut.e(Throwable("throwable message")) - verify(fixture.logs).log(eq(SentryLogLevel.ERROR), eq("throwable message")) + verify(fixture.logs) + .log( + eq(SentryLogLevel.ERROR), + check { assertEquals("auto.log.timber", it.origin) }, + eq("throwable message"), + ) } @Test @@ -308,6 +331,11 @@ class SentryTimberTreeTest { val sut = fixture.getSut() sut.e(Throwable("throwable message"), "My message") - verify(fixture.logs).log(eq(SentryLogLevel.ERROR), eq("My message\nthrowable message")) + verify(fixture.logs) + .log( + eq(SentryLogLevel.ERROR), + check { assertEquals("auto.log.timber", it.origin) }, + eq("My message\nthrowable message"), + ) } } diff --git a/sentry-jul/src/main/java/io/sentry/jul/SentryHandler.java b/sentry-jul/src/main/java/io/sentry/jul/SentryHandler.java index 9c027880c7..23cba406b1 100644 --- a/sentry-jul/src/main/java/io/sentry/jul/SentryHandler.java +++ b/sentry-jul/src/main/java/io/sentry/jul/SentryHandler.java @@ -158,6 +158,7 @@ protected void captureLog(@NotNull LogRecord loggingEvent) { final @NotNull String formattedMessage = maybeFormatted(arguments, message); final @NotNull SentryLogParameters params = SentryLogParameters.create(attributes); + params.setOrigin("auto.log.jul"); Sentry.logger().log(sentryLevel, params, formattedMessage, arguments); } diff --git a/sentry-jul/src/test/kotlin/io/sentry/jul/SentryHandlerTest.kt b/sentry-jul/src/test/kotlin/io/sentry/jul/SentryHandlerTest.kt index 947913f5f1..8f39c0d94a 100644 --- a/sentry-jul/src/test/kotlin/io/sentry/jul/SentryHandlerTest.kt +++ b/sentry-jul/src/test/kotlin/io/sentry/jul/SentryHandlerTest.kt @@ -414,7 +414,12 @@ class SentryHandlerTest { Sentry.flush(1000) verify(fixture.transport) - .send(checkLogs { event -> assertEquals(SentryLogLevel.TRACE, event.items.first().level) }) + .send( + checkLogs { event -> + assertEquals(SentryLogLevel.TRACE, event.items.first().level) + assertEquals("auto.log.jul", event.items.first().attributes?.get("sentry.origin")?.value) + } + ) } @Test diff --git a/sentry-log4j2/src/main/java/io/sentry/log4j2/SentryAppender.java b/sentry-log4j2/src/main/java/io/sentry/log4j2/SentryAppender.java index dddc6f3b90..9e4cf0eed9 100644 --- a/sentry-log4j2/src/main/java/io/sentry/log4j2/SentryAppender.java +++ b/sentry-log4j2/src/main/java/io/sentry/log4j2/SentryAppender.java @@ -228,6 +228,7 @@ protected void captureLog(@NotNull LogEvent loggingEvent) { final @NotNull String formattedMessage = loggingEvent.getMessage().getFormattedMessage(); final @NotNull SentryLogParameters params = SentryLogParameters.create(attributes); + params.setOrigin("auto.log.log4j2"); Sentry.logger().log(sentryLevel, params, formattedMessage, arguments); } diff --git a/sentry-log4j2/src/test/kotlin/io/sentry/log4j2/SentryAppenderTest.kt b/sentry-log4j2/src/test/kotlin/io/sentry/log4j2/SentryAppenderTest.kt index 25de2bd735..9ddfdbd43b 100644 --- a/sentry-log4j2/src/test/kotlin/io/sentry/log4j2/SentryAppenderTest.kt +++ b/sentry-log4j2/src/test/kotlin/io/sentry/log4j2/SentryAppenderTest.kt @@ -251,7 +251,15 @@ class SentryAppenderTest { Sentry.flush(1000) verify(fixture.transport) - .send(checkLogs { event -> assertEquals(SentryLogLevel.TRACE, event.items.first().level) }) + .send( + checkLogs { event -> + assertEquals(SentryLogLevel.TRACE, event.items.first().level) + assertEquals( + "auto.log.log4j2", + event.items.first().attributes?.get("sentry.origin")?.value, + ) + } + ) } @Test 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 394087c688..7d9d53c706 100644 --- a/sentry-logback/src/main/java/io/sentry/logback/SentryAppender.java +++ b/sentry-logback/src/main/java/io/sentry/logback/SentryAppender.java @@ -193,6 +193,7 @@ protected void captureLog(@NotNull ILoggingEvent loggingEvent) { final @NotNull String formattedMessage = formatted(loggingEvent); final @NotNull SentryLogParameters params = SentryLogParameters.create(attributes); + params.setOrigin("auto.log.logback"); Sentry.logger().log(sentryLevel, params, formattedMessage, arguments); } 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 431ede18d9..ffe84ed55d 100644 --- a/sentry-logback/src/test/kotlin/io/sentry/logback/SentryAppenderTest.kt +++ b/sentry-logback/src/test/kotlin/io/sentry/logback/SentryAppenderTest.kt @@ -382,6 +382,7 @@ class SentryAppenderTest { val attributes = log.attributes!! assertEquals("Testing {} level", attributes["sentry.message.template"]?.value) assertEquals("TRACE", attributes["sentry.message.parameter.0"]?.value) + assertEquals("auto.log.logback", attributes["sentry.origin"]?.value) } ) } diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 767d044159..3b0833a187 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -4871,8 +4871,10 @@ public final class io/sentry/logger/SentryLogParameters { public static fun create (Lio/sentry/SentryAttributes;)Lio/sentry/logger/SentryLogParameters; public static fun create (Lio/sentry/SentryDate;Lio/sentry/SentryAttributes;)Lio/sentry/logger/SentryLogParameters; public fun getAttributes ()Lio/sentry/SentryAttributes; + public fun getOrigin ()Ljava/lang/String; public fun getTimestamp ()Lio/sentry/SentryDate; public fun setAttributes (Lio/sentry/SentryAttributes;)V + public fun setOrigin (Ljava/lang/String;)V public fun setTimestamp (Lio/sentry/SentryDate;)V } diff --git a/sentry/src/main/java/io/sentry/logger/LoggerApi.java b/sentry/src/main/java/io/sentry/logger/LoggerApi.java index 976b55de6e..19c25c0f56 100644 --- a/sentry/src/main/java/io/sentry/logger/LoggerApi.java +++ b/sentry/src/main/java/io/sentry/logger/LoggerApi.java @@ -133,7 +133,7 @@ private void captureLog( span == null ? propagationContext.getSpanId() : span.getSpanContext().getSpanId(); final SentryLogEvent logEvent = new SentryLogEvent(traceId, timestampToUse, messageToUse, level); - logEvent.setAttributes(createAttributes(params.getAttributes(), message, spanId, args)); + logEvent.setAttributes(createAttributes(params, message, spanId, args)); logEvent.setSeverityNumber(level.getSeverityNumber()); scopes.getClient().captureLog(logEvent, combinedScope); @@ -160,11 +160,16 @@ private void captureLog( } private @NotNull HashMap createAttributes( - final @Nullable SentryAttributes incomingAttributes, + final @NotNull SentryLogParameters params, final @NotNull String message, final @NotNull SpanId spanId, final @Nullable Object... args) { final @NotNull HashMap attributes = new HashMap<>(); + attributes.put( + "sentry.origin", + new SentryLogEventAttributeValue(SentryAttributeType.STRING, params.getOrigin())); + + final @Nullable SentryAttributes incomingAttributes = params.getAttributes(); if (incomingAttributes != null) { for (SentryAttribute attribute : incomingAttributes.getAttributes().values()) { diff --git a/sentry/src/main/java/io/sentry/logger/SentryLogParameters.java b/sentry/src/main/java/io/sentry/logger/SentryLogParameters.java index 7eeae78fdb..0331a8649d 100644 --- a/sentry/src/main/java/io/sentry/logger/SentryLogParameters.java +++ b/sentry/src/main/java/io/sentry/logger/SentryLogParameters.java @@ -9,6 +9,7 @@ public final class SentryLogParameters { private @Nullable SentryDate timestamp; private @Nullable SentryAttributes attributes; + private @NotNull String origin = "manual"; public @Nullable SentryDate getTimestamp() { return timestamp; @@ -26,6 +27,14 @@ public void setAttributes(final @Nullable SentryAttributes attributes) { this.attributes = attributes; } + public @NotNull String getOrigin() { + return origin; + } + + public void setOrigin(final @NotNull String origin) { + this.origin = origin; + } + public static @NotNull SentryLogParameters create( final @Nullable SentryDate timestamp, final @Nullable SentryAttributes attributes) { final @NotNull SentryLogParameters params = new SentryLogParameters();