diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ff42f9fe4..fd43f77bbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Features +- Add an explicit Logs opt-in to the Android Logcat integration ([#5945](https://github.com/getsentry/sentry-java/pull/5945)) - Add an explicit Logs opt-in to the Android Timber integration ([#5943](https://github.com/getsentry/sentry-java/pull/5943)) - Add an explicit Logs opt-in to the JUL handler ([#5942](https://github.com/getsentry/sentry-java/pull/5942)) - Add an explicit Logs opt-in to the Log4j2 appender ([#5941](https://github.com/getsentry/sentry-java/pull/5941)) diff --git a/sentry-android-core/api/sentry-android-core.api b/sentry-android-core/api/sentry-android-core.api index 9e3b0fcf17..16b9caabc4 100644 --- a/sentry-android-core/api/sentry-android-core.api +++ b/sentry-android-core/api/sentry-android-core.api @@ -430,6 +430,7 @@ public final class io/sentry/android/core/SentryAndroidOptions : io/sentry/Sentr public fun isEnableAutoActivityLifecycleTracing ()Z public fun isEnableAutoTraceIdGeneration ()Z public fun isEnableFramesTracking ()Z + public fun isEnableLogcatLogs ()Z public fun isEnableNdk ()Z public fun isEnableNdkAppHangTracking ()Z public fun isEnableNetworkEventBreadcrumbs ()Z @@ -464,6 +465,7 @@ public final class io/sentry/android/core/SentryAndroidOptions : io/sentry/Sentr public fun setEnableAutoActivityLifecycleTracing (Z)V public fun setEnableAutoTraceIdGeneration (Z)V public fun setEnableFramesTracking (Z)V + public fun setEnableLogcatLogs (Z)V public fun setEnableNdk (Z)V public fun setEnableNdkAppHangTracking (Z)V public fun setEnableNetworkEventBreadcrumbs (Z)V diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java b/sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java index ddebd1b5ee..ff6c3cbcd7 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java @@ -165,6 +165,8 @@ final class ManifestMetadataReader { static final String ENABLE_TIMBER_LOGS = "io.sentry.timber.logs.enabled"; + static final String ENABLE_LOGCAT_LOGS = "io.sentry.logcat.logs.enabled"; + static final String ENABLE_METRICS = "io.sentry.metrics.enabled"; static final String ENABLE_AUTO_TRACE_ID_GENERATION = @@ -711,6 +713,9 @@ static void applyMetadata( options.setEnableTimberLogs( readBool(metadata, logger, ENABLE_TIMBER_LOGS, options.isEnableTimberLogs())); + options.setEnableLogcatLogs( + readBool(metadata, logger, ENABLE_LOGCAT_LOGS, options.isEnableLogcatLogs())); + options .getMetrics() .setEnabled( diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java index a0ca7a36c2..ade7a5fc95 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java @@ -74,6 +74,9 @@ public final class SentryAndroidOptions extends SentryOptions { /** Enable or disable automatic Sentry Logs capture from Timber. Default is disabled. */ private boolean enableTimberLogs = false; + /** Enable or disable automatic Sentry Logs capture from Logcat. Default is disabled. */ + private boolean enableLogcatLogs = false; + /** * Enables the Auto instrumentation for Activity lifecycle tracing. * @@ -468,6 +471,14 @@ public void setEnableTimberLogs(boolean enableTimberLogs) { this.enableTimberLogs = enableTimberLogs; } + public boolean isEnableLogcatLogs() { + return enableLogcatLogs; + } + + public void setEnableLogcatLogs(boolean enableLogcatLogs) { + this.enableLogcatLogs = enableLogcatLogs; + } + /** * Enable or disable all the automatic breadcrumbs * 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 1e649c1783..bab26e9c3e 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.SentryOptions; import io.sentry.logger.SentryLogParameters; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -52,8 +53,10 @@ private static void addAsLog( @Nullable final String msg, @Nullable final Throwable tr) { final @NotNull ScopesAdapter scopes = ScopesAdapter.getInstance(); - // Check if logs are enabled before doing expensive operations - if (!scopes.getOptions().getLogs().isEnabled()) { + final @NotNull SentryOptions options = scopes.getOptions(); + if (!(options instanceof SentryAndroidOptions) + || !((SentryAndroidOptions) options).isEnableLogcatLogs() + || !options.getLogs().isEnabled()) { return; } final @Nullable String trMessage = tr != null ? tr.getMessage() : null; diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/ManifestMetadataReaderTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/ManifestMetadataReaderTest.kt index f0d6e6f562..b8cf2224b2 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/ManifestMetadataReaderTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/ManifestMetadataReaderTest.kt @@ -4,6 +4,7 @@ import android.content.Context import android.os.Bundle import androidx.core.os.bundleOf import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat import io.sentry.FilterString import io.sentry.ILogger import io.sentry.ProfileLifecycle @@ -1986,6 +1987,36 @@ class ManifestMetadataReaderTest { assertTrue(fixture.options.isEnableTimberLogs) } + @Test + fun `applyMetadata keeps Logcat logs disabled if not found`() { + val context = fixture.getContext() + + ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider) + + assertThat(fixture.options.isEnableLogcatLogs).isFalse() + } + + @Test + fun `applyMetadata reads Logcat logs enabled to options`() { + val bundle = bundleOf(ManifestMetadataReader.ENABLE_LOGCAT_LOGS to true) + val context = fixture.getContext(metaData = bundle) + + ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider) + + assertThat(fixture.options.isEnableLogcatLogs).isTrue() + } + + @Test + fun `applyMetadata reads Logcat logs disabled to options`() { + fixture.options.isEnableLogcatLogs = true + val bundle = bundleOf(ManifestMetadataReader.ENABLE_LOGCAT_LOGS to false) + val context = fixture.getContext(metaData = bundle) + + ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider) + + assertThat(fixture.options.isEnableLogcatLogs).isFalse() + } + @Test fun `applyMetadata reads metrics enabled and keep default value if not found`() { // Arrange diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidOptionsTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidOptionsTest.kt index 2e5fa0ea45..4d1fbad0dd 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidOptionsTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidOptionsTest.kt @@ -1,5 +1,6 @@ package io.sentry.android.core +import com.google.common.truth.Truth.assertThat import io.sentry.ITransactionProfiler import io.sentry.NoOpTransactionProfiler import io.sentry.protocol.DebugImage @@ -108,6 +109,19 @@ class SentryAndroidOptionsTest { assertTrue(sentryOptions.isEnableTimberLogs) } + @Test + fun `Logcat logs are disabled by default`() { + assertThat(SentryAndroidOptions().isEnableLogcatLogs).isFalse() + } + + @Test + fun `Logcat logs can be enabled`() { + val sentryOptions = SentryAndroidOptions() + sentryOptions.isEnableLogcatLogs = true + + assertThat(sentryOptions.isEnableLogcatLogs).isTrue() + } + @Test fun `attach screenshots disabled by default for Android`() { val sentryOptions = SentryAndroidOptions() 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 0c0c03d71d..423df279f1 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 @@ -2,6 +2,7 @@ package io.sentry.android.core import android.os.Bundle import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat import io.sentry.Breadcrumb import io.sentry.Sentry import io.sentry.SentryLevel @@ -15,6 +16,7 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue import org.junit.runner.RunWith +import org.robolectric.shadows.ShadowLog @RunWith(AndroidJUnit4::class) class SentryLogcatAdapterTest { @@ -26,9 +28,12 @@ class SentryLogcatAdapterTest { val breadcrumbs = mutableListOf() val logs = mutableListOf() - fun initSut(options: Sentry.OptionsConfiguration? = null) { - val metadata = - Bundle().apply { putString(ManifestMetadataReader.DSN, "https://key@sentry.io/123") } + fun initSut( + enableLogcatLogs: Boolean? = true, + metadata: Bundle = Bundle(), + options: Sentry.OptionsConfiguration? = null, + ) { + metadata.putString(ManifestMetadataReader.DSN, "https://key@sentry.io/123") val mockContext = ContextUtilsTestHelper.mockMetaData(metaData = metadata) initForTest(mockContext) { it.beforeBreadcrumb = SentryOptions.BeforeBreadcrumbCallback { breadcrumb, _ -> @@ -36,6 +41,9 @@ class SentryLogcatAdapterTest { breadcrumb } it.logs.isEnabled = true + if (enableLogcatLogs != null) { + it.isEnableLogcatLogs = enableLogcatLogs + } it.logs.beforeSend = SentryOptions.Logs.BeforeSendLogCallback { logEvent -> logs.add(logEvent) @@ -55,6 +63,37 @@ class SentryLogcatAdapterTest { Sentry.close() fixture.breadcrumbs.clear() fixture.logs.clear() + ShadowLog.clear() + } + + @Test + fun `Logcat logs are disabled by default while breadcrumbs and Android Log remain enabled`() { + fixture.initSut(enableLogcatLogs = null) + + SentryLogcatAdapter.d(tag, commonMsg) + + assertThat(fixture.logs).isEmpty() + assertThat(fixture.breadcrumbs).hasSize(1) + assertThat(ShadowLog.getLogs().any { it.tag == tag && it.msg == commonMsg }).isTrue() + } + + @Test + fun `Logcat logs can be enabled through Android options`() { + fixture.initSut(enableLogcatLogs = true) + + SentryLogcatAdapter.d(tag, commonMsg) + + assertThat(fixture.logs).hasSize(1) + } + + @Test + fun `Logcat logs can be enabled through manifest metadata`() { + val metadata = Bundle().apply { putBoolean(ManifestMetadataReader.ENABLE_LOGCAT_LOGS, true) } + fixture.initSut(enableLogcatLogs = null, metadata = metadata) + + SentryLogcatAdapter.d(tag, commonMsg) + + assertThat(fixture.logs).hasSize(1) } @Test