From 2ecbde936c4c3c96388fd33d8cc51bfbb4d749da Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Wed, 14 Oct 2026 12:41:47 +0200 Subject: [PATCH 1/2] fix(core): Reduce serialization log spam Allow serializers to suppress debug logging for individual calls and disable it while calculating serialized byte sizes. Preserve compatibility for custom serializers through a default interface implementation. Fixes #4946 Co-Authored-By: Claude --- sentry/api/sentry.api | 2 ++ sentry/src/main/java/io/sentry/ISerializer.java | 14 ++++++++++++++ sentry/src/main/java/io/sentry/JsonSerializer.java | 8 +++++++- .../io/sentry/util/JsonSerializationUtils.java | 2 +- .../src/test/java/io/sentry/JsonSerializerTest.kt | 14 ++++++++++++++ .../io/sentry/util/JsonSerializationUtilsTest.kt | 13 +++++++++++++ 6 files changed, 51 insertions(+), 2 deletions(-) diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 2007edf00c9..d3c6dc94476 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -1233,6 +1233,7 @@ public abstract interface class io/sentry/ISerializer { public abstract fun deserializeEnvelope (Ljava/io/InputStream;)Lio/sentry/SentryEnvelope; public abstract fun serialize (Lio/sentry/SentryEnvelope;Ljava/io/OutputStream;)V public abstract fun serialize (Ljava/lang/Object;Ljava/io/Writer;)V + public fun serialize (Ljava/lang/Object;Ljava/io/Writer;Z)V public abstract fun serialize (Ljava/util/Map;)Ljava/lang/String; } @@ -1450,6 +1451,7 @@ public final class io/sentry/JsonSerializer : io/sentry/ISerializer { public fun deserializeEnvelope (Ljava/io/InputStream;)Lio/sentry/SentryEnvelope; public fun serialize (Lio/sentry/SentryEnvelope;Ljava/io/OutputStream;)V public fun serialize (Ljava/lang/Object;Ljava/io/Writer;)V + public fun serialize (Ljava/lang/Object;Ljava/io/Writer;Z)V public fun serialize (Ljava/util/Map;)Ljava/lang/String; } diff --git a/sentry/src/main/java/io/sentry/ISerializer.java b/sentry/src/main/java/io/sentry/ISerializer.java index f74c9ab022b..0f7c193ab0b 100644 --- a/sentry/src/main/java/io/sentry/ISerializer.java +++ b/sentry/src/main/java/io/sentry/ISerializer.java @@ -23,6 +23,20 @@ public interface ISerializer { void serialize(@NotNull T entity, @NotNull Writer writer) throws IOException; + /** + * Serializes an entity to a writer with optional logging control. + * + * @param entity the entity to serialize + * @param writer the writer to serialize to + * @param enableLogging whether serializer logging is enabled, if supported + * @param the type of entity + * @throws IOException if serialization fails + */ + default void serialize(@NotNull T entity, @NotNull Writer writer, boolean enableLogging) + throws IOException { + serialize(entity, writer); + } + /** * Serializes an envelope * diff --git a/sentry/src/main/java/io/sentry/JsonSerializer.java b/sentry/src/main/java/io/sentry/JsonSerializer.java index 79a1c72bef3..d7b7423e520 100644 --- a/sentry/src/main/java/io/sentry/JsonSerializer.java +++ b/sentry/src/main/java/io/sentry/JsonSerializer.java @@ -208,10 +208,16 @@ public JsonSerializer(@NotNull SentryOptions options) { @Override public void serialize(@NotNull T entity, @NotNull Writer writer) throws IOException { + serialize(entity, writer, true); + } + + @Override + public void serialize(@NotNull T entity, @NotNull Writer writer, boolean enableLogging) + throws IOException { Objects.requireNonNull(entity, "The entity is required."); Objects.requireNonNull(writer, "The Writer object is required."); - if (options.getLogger().isEnabled(SentryLevel.DEBUG)) { + if (enableLogging && options.getLogger().isEnabled(SentryLevel.DEBUG)) { String serialized = serializeToString(entity, options.isEnablePrettySerializationOutput()); options.getLogger().log(SentryLevel.DEBUG, "Serializing object: %s", serialized); } diff --git a/sentry/src/main/java/io/sentry/util/JsonSerializationUtils.java b/sentry/src/main/java/io/sentry/util/JsonSerializationUtils.java index fc9bc75aa3f..ab0f5c9c323 100644 --- a/sentry/src/main/java/io/sentry/util/JsonSerializationUtils.java +++ b/sentry/src/main/java/io/sentry/util/JsonSerializationUtils.java @@ -85,7 +85,7 @@ public static long byteSizeOf( } try { final ByteCountingWriter writer = new ByteCountingWriter(); - serializer.serialize(serializable, writer); + serializer.serialize(serializable, writer, false); return writer.getByteCount(); } catch (Throwable t) { logger.log(SentryLevel.ERROR, "Could not calculate size of serializable", t); diff --git a/sentry/src/test/java/io/sentry/JsonSerializerTest.kt b/sentry/src/test/java/io/sentry/JsonSerializerTest.kt index 95b664e13a9..3e234c0a7c2 100644 --- a/sentry/src/test/java/io/sentry/JsonSerializerTest.kt +++ b/sentry/src/test/java/io/sentry/JsonSerializerTest.kt @@ -1550,6 +1550,20 @@ class JsonSerializerTest { .log(any(), check { assertTrue(it.startsWith("Serializing object:")) }, any()) } + @Test + fun `json serializer can disable serialization logging`() { + val logger = mock() + val options = SentryOptions() + options.setLogger(logger) + options.setDebug(true) + whenever(logger.isEnabled(any())).thenReturn(true) + + (options.serializer as JsonSerializer).serialize(mapOf("key" to "val"), StringWriter(), false) + + verify(logger, never()) + .log(any(), check { assertTrue(it.startsWith("Serializing object:")) }, any()) + } + @Test fun `json serializer does not close the stream that is passed in`() { val stream = mock() diff --git a/sentry/src/test/java/io/sentry/util/JsonSerializationUtilsTest.kt b/sentry/src/test/java/io/sentry/util/JsonSerializationUtilsTest.kt index caa93f1af7a..170ad4646f4 100644 --- a/sentry/src/test/java/io/sentry/util/JsonSerializationUtilsTest.kt +++ b/sentry/src/test/java/io/sentry/util/JsonSerializationUtilsTest.kt @@ -1,6 +1,7 @@ package io.sentry.util import io.sentry.ILogger +import io.sentry.ISerializer import io.sentry.JsonSerializable import io.sentry.JsonSerializer import io.sentry.ObjectWriter @@ -18,7 +19,9 @@ import kotlin.test.assertNull import kotlin.test.assertTrue import org.mockito.invocation.InvocationOnMock import org.mockito.kotlin.any +import org.mockito.kotlin.eq import org.mockito.kotlin.mock +import org.mockito.kotlin.verify class JsonSerializationUtilsTest { @@ -117,6 +120,16 @@ class JsonSerializationUtilsTest { assertTrue(byteSize > 0, "byteSize should be positive") } + @Test + fun `byteSizeOf disables serialization logging`() { + val mockSerializer: ISerializer = mock() + val serializable: JsonSerializable = mock() + + JsonSerializationUtils.byteSizeOf(mockSerializer, logger, serializable) + + verify(mockSerializer).serialize(eq(serializable), any(), eq(false)) + } + @Test fun `byteSizeOf returns 0 on serialization error`() { val serializable = From 775d450202755f7fe009c8321fcdf227d92bbe43 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Wed, 14 Oct 2026 12:42:46 +0200 Subject: [PATCH 2/2] changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 295f7d46508..e8cc3682133 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,10 @@ - Report the cellular network technology generation in `device.connection_effective_type`, for example `4g` or `5g` ([#6146](https://github.com/getsentry/sentry-java/pull/6146)) +### Fixes + +- Avoid duplicate serialization debug logs when calculating serialized object sizes ([#6255](https://github.com/getsentry/sentry-java/pull/6255)) + ### Dependencies - Bump Native SDK from v0.17.1 to v0.17.2 ([#6230](https://github.com/getsentry/sentry-java/pull/6230))