diff --git a/CHANGELOG.md b/CHANGELOG.md index c367f7d943..cddc5f9c67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Features - Added `io.sentry.ndk.sdk-name` Android manifest option to configure the native SDK's name ([#5027](https://github.com/getsentry/sentry-java/pull/5027)) +- Replace `sentry.trace.parent_span_id` attribute with `spanId` property on `SentryLogEvent` ([#5040](https://github.com/getsentry/sentry-java/pull/5040)) ### Fixes diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index c8e0ec4bde..b3e6d40be8 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -3238,7 +3238,9 @@ public final class io/sentry/SentryLogEvent : io/sentry/JsonSerializable, io/sen public fun getBody ()Ljava/lang/String; public fun getLevel ()Lio/sentry/SentryLogLevel; public fun getSeverityNumber ()Ljava/lang/Integer; + public fun getSpanId ()Lio/sentry/SpanId; public fun getTimestamp ()Ljava/lang/Double; + public fun getTraceId ()Lio/sentry/protocol/SentryId; public fun getUnknown ()Ljava/util/Map; public fun serialize (Lio/sentry/ObjectWriter;Lio/sentry/ILogger;)V public fun setAttribute (Ljava/lang/String;Lio/sentry/SentryLogEventAttributeValue;)V @@ -3246,7 +3248,9 @@ public final class io/sentry/SentryLogEvent : io/sentry/JsonSerializable, io/sen public fun setBody (Ljava/lang/String;)V public fun setLevel (Lio/sentry/SentryLogLevel;)V public fun setSeverityNumber (Ljava/lang/Integer;)V + public fun setSpanId (Lio/sentry/SpanId;)V public fun setTimestamp (Ljava/lang/Double;)V + public fun setTraceId (Lio/sentry/protocol/SentryId;)V public fun setUnknown (Ljava/util/Map;)V } @@ -3261,6 +3265,7 @@ public final class io/sentry/SentryLogEvent$JsonKeys { public static final field BODY Ljava/lang/String; public static final field LEVEL Ljava/lang/String; public static final field SEVERITY_NUMBER Ljava/lang/String; + public static final field SPAN_ID Ljava/lang/String; public static final field TIMESTAMP Ljava/lang/String; public static final field TRACE_ID Ljava/lang/String; public fun ()V diff --git a/sentry/src/main/java/io/sentry/SentryLogEvent.java b/sentry/src/main/java/io/sentry/SentryLogEvent.java index 441e1ef79a..34559fec9d 100644 --- a/sentry/src/main/java/io/sentry/SentryLogEvent.java +++ b/sentry/src/main/java/io/sentry/SentryLogEvent.java @@ -13,6 +13,7 @@ public final class SentryLogEvent implements JsonUnknown, JsonSerializable { private @NotNull SentryId traceId; + private @Nullable SpanId spanId; private @NotNull Double timestamp; private @NotNull String body; private @NotNull SentryLogLevel level; @@ -92,10 +93,27 @@ public void setSeverityNumber(final @Nullable Integer severityNumber) { this.severityNumber = severityNumber; } + public @Nullable SpanId getSpanId() { + return spanId; + } + + public void setSpanId(final @Nullable SpanId spanId) { + this.spanId = spanId; + } + + public @NotNull SentryId getTraceId() { + return traceId; + } + + public void setTraceId(final @NotNull SentryId traceId) { + this.traceId = traceId; + } + // region json public static final class JsonKeys { public static final String TIMESTAMP = "timestamp"; public static final String TRACE_ID = "trace_id"; + public static final String SPAN_ID = "span_id"; public static final String LEVEL = "level"; public static final String SEVERITY_NUMBER = "severity_number"; public static final String BODY = "body"; @@ -109,6 +127,9 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger writer.beginObject(); writer.name(JsonKeys.TIMESTAMP).value(logger, doubleToBigDecimal(timestamp)); writer.name(JsonKeys.TRACE_ID).value(logger, traceId); + if (spanId != null) { + writer.name(JsonKeys.SPAN_ID).value(logger, spanId); + } writer.name(JsonKeys.BODY).value(body); writer.name(JsonKeys.LEVEL).value(logger, level); if (severityNumber != null) { @@ -145,6 +166,7 @@ public static final class Deserializer implements JsonDeserializer unknown = null; @Nullable SentryId traceId = null; + @Nullable SpanId spanId = null; @Nullable Double timestamp = null; @Nullable String body = null; @Nullable SentryLogLevel level = null; @@ -158,6 +180,9 @@ public static final class Deserializer implements JsonDeserializer createAttributes( final @NotNull SentryLogParameters params, final @NotNull String message, - final @NotNull SpanId spanId, final @Nullable Object... args) { final @NotNull HashMap attributes = new HashMap<>(); final @NotNull String origin = params.getOrigin(); @@ -239,10 +239,6 @@ private void captureLog( "sentry.release", new SentryLogEventAttributeValue(SentryAttributeType.STRING, release)); } - attributes.put( - "sentry.trace.parent_span_id", - new SentryLogEventAttributeValue(SentryAttributeType.STRING, spanId)); - if (Platform.isJvm()) { setServerName(attributes); } diff --git a/sentry/src/test/java/io/sentry/ScopesTest.kt b/sentry/src/test/java/io/sentry/ScopesTest.kt index 07a7c15779..60ef92e893 100644 --- a/sentry/src/test/java/io/sentry/ScopesTest.kt +++ b/sentry/src/test/java/io/sentry/ScopesTest.kt @@ -3157,6 +3157,52 @@ class ScopesTest { ) } + @Test + fun `log event has spanId from active span`() { + val (sut, mockClient) = getEnabledScopes { it.logs.isEnabled = true } + + val transaction = + sut.startTransaction( + "test transaction", + "test.op", + TransactionOptions().also { it.isBindToScope = true }, + ) + + sut.logger().log(SentryLogLevel.WARN, "log message") + + verify(mockClient) + .captureLog( + check { + assertEquals("log message", it.body) + assertEquals(transaction.spanContext.traceId, it.traceId) + assertEquals(transaction.spanContext.spanId, it.spanId) + }, + anyOrNull(), + ) + + transaction.finish() + } + + @Test + fun `log event has spanId from propagation context when no active span`() { + val (sut, mockClient) = getEnabledScopes { it.logs.isEnabled = true } + + var propagationContext: PropagationContext? = null + sut.configureScope { propagationContext = it.propagationContext } + + sut.logger().log(SentryLogLevel.WARN, "log message") + + verify(mockClient) + .captureLog( + check { + assertEquals("log message", it.body) + assertEquals(propagationContext!!.traceId, it.traceId) + assertEquals(propagationContext!!.spanId, it.spanId) + }, + anyOrNull(), + ) + } + // endregion // region metrics @@ -4075,6 +4121,54 @@ class ScopesTest { ) } + @Test + fun `metric event has spanId from active span`() { + val (sut, mockClient) = getEnabledScopes { it.metrics.isEnabled = true } + + val transaction = + sut.startTransaction( + "test transaction", + "test.op", + TransactionOptions().also { it.isBindToScope = true }, + ) + + sut.metrics().count("metric name") + + verify(mockClient) + .captureMetric( + check { + assertEquals("metric name", it.name) + assertEquals(transaction.spanContext.traceId, it.traceId) + assertEquals(transaction.spanContext.spanId, it.spanId) + }, + anyOrNull(), + anyOrNull(), + ) + + transaction.finish() + } + + @Test + fun `metric event has spanId from propagation context when no active span`() { + val (sut, mockClient) = getEnabledScopes { it.metrics.isEnabled = true } + + var propagationContext: PropagationContext? = null + sut.configureScope { propagationContext = it.propagationContext } + + sut.metrics().count("metric name") + + verify(mockClient) + .captureMetric( + check { + assertEquals("metric name", it.name) + assertEquals(propagationContext!!.traceId, it.traceId) + assertEquals(propagationContext!!.spanId, it.spanId) + }, + anyOrNull(), + anyOrNull(), + ) + } + // endregion @Test diff --git a/sentry/src/test/java/io/sentry/protocol/SentryLogsSerializationTest.kt b/sentry/src/test/java/io/sentry/protocol/SentryLogsSerializationTest.kt index 1d72f6342d..c65a3cca70 100644 --- a/sentry/src/test/java/io/sentry/protocol/SentryLogsSerializationTest.kt +++ b/sentry/src/test/java/io/sentry/protocol/SentryLogsSerializationTest.kt @@ -11,6 +11,7 @@ import io.sentry.SentryLogEvent import io.sentry.SentryLogEventAttributeValue import io.sentry.SentryLogEvents import io.sentry.SentryLogLevel +import io.sentry.SpanId import java.io.StringReader import java.io.StringWriter import kotlin.test.assertEquals @@ -31,6 +32,7 @@ class SentryLogsSerializationTest { SentryLogLevel.INFO, ) .also { + it.spanId = SpanId("0a53026963414893") it.attributes = mutableMapOf( "sentry.sdk.name" to diff --git a/sentry/src/test/resources/json/sentry_logs.json b/sentry/src/test/resources/json/sentry_logs.json index 67ced8b7cc..e78f5af1b0 100644 --- a/sentry/src/test/resources/json/sentry_logs.json +++ b/sentry/src/test/resources/json/sentry_logs.json @@ -4,6 +4,7 @@ { "timestamp": 1081621443.000000, "trace_id": "5c1f73d39486827b9e60ceb1fc23277a", + "span_id": "0a53026963414893", "body": "42e6bd2a-c45e-414d-8066-ed5196fbc686", "level": "info", "severity_number": 10,