diff --git a/CHANGELOG.md b/CHANGELOG.md index 433e199e973..a772634fd71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,8 @@ - For `graphql-java` v22 and newer please use the `sentry-graphql-22` module - We now provide a `SentryInstrumenter` bean directly for Spring (Boot) if there is none yet instead of using `GraphQlSourceBuilderCustomizer` to add the instrumentation ([#3744](https://github.com/getsentry/sentry-java/pull/3744)) - It is now also possible to provide a bean of type `SentryGraphqlInstrumentation.BeforeSpanCallback` which is then used by `SentryInstrumenter` +- Emit transaction.data inside contexts.trace.data ([#3735](https://github.com/getsentry/sentry-java/pull/3735)) + - Also does not emit `transaction.data` in `exras` anymore ### Fixes diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 8a9c296b135..7ccc63a19e9 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -3390,6 +3390,7 @@ public class io/sentry/SpanContext : io/sentry/JsonSerializable, io/sentry/JsonU public static final field DEFAULT_ORIGIN Ljava/lang/String; public static final field TYPE Ljava/lang/String; protected field baggage Lio/sentry/Baggage; + protected field data Ljava/util/Map; protected field description Ljava/lang/String; protected field op Ljava/lang/String; protected field origin Ljava/lang/String; @@ -3403,6 +3404,7 @@ public class io/sentry/SpanContext : io/sentry/JsonSerializable, io/sentry/JsonU public fun copyForChild (Ljava/lang/String;Lio/sentry/SpanId;Lio/sentry/SpanId;)Lio/sentry/SpanContext; public fun equals (Ljava/lang/Object;)Z public fun getBaggage ()Lio/sentry/Baggage; + public fun getData ()Ljava/util/Map; public fun getDescription ()Ljava/lang/String; public fun getInstrumenter ()Lio/sentry/Instrumenter; public fun getOperation ()Ljava/lang/String; @@ -3418,6 +3420,7 @@ public class io/sentry/SpanContext : io/sentry/JsonSerializable, io/sentry/JsonU public fun getUnknown ()Ljava/util/Map; public fun hashCode ()I public fun serialize (Lio/sentry/ObjectWriter;Lio/sentry/ILogger;)V + public fun setData (Ljava/lang/String;Ljava/lang/Object;)V public fun setDescription (Ljava/lang/String;)V public fun setInstrumenter (Lio/sentry/Instrumenter;)V public fun setOperation (Ljava/lang/String;)V @@ -3437,6 +3440,7 @@ public final class io/sentry/SpanContext$Deserializer : io/sentry/JsonDeserializ } public final class io/sentry/SpanContext$JsonKeys { + public static final field DATA Ljava/lang/String; public static final field DESCRIPTION Ljava/lang/String; public static final field OP Ljava/lang/String; public static final field ORIGIN Ljava/lang/String; diff --git a/sentry/src/main/java/io/sentry/SpanContext.java b/sentry/src/main/java/io/sentry/SpanContext.java index b08ee2b84f2..91e3abd9560 100644 --- a/sentry/src/main/java/io/sentry/SpanContext.java +++ b/sentry/src/main/java/io/sentry/SpanContext.java @@ -44,9 +44,10 @@ public class SpanContext implements JsonUnknown, JsonSerializable { /** A map or list of tags for this event. Each tag must be less than 200 characters. */ protected @NotNull Map tags = new ConcurrentHashMap<>(); - /** Describes the status of the Transaction. */ protected @Nullable String origin = DEFAULT_ORIGIN; + protected @NotNull Map data = new ConcurrentHashMap<>(); + private @Nullable Map unknown; private @NotNull Instrumenter instrumenter = Instrumenter.SENTRY; @@ -230,6 +231,14 @@ public void setInstrumenter(final @NotNull Instrumenter instrumenter) { return baggage; } + public @NotNull Map getData() { + return data; + } + + public void setData(final @NotNull String key, final @NotNull Object value) { + data.put(key, value); + } + @ApiStatus.Internal public SpanContext copyForChild( final @NotNull String operation, @@ -275,6 +284,7 @@ public static final class JsonKeys { public static final String STATUS = "status"; public static final String TAGS = "tags"; public static final String ORIGIN = "origin"; + public static final String DATA = "data"; } @Override @@ -302,6 +312,9 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger if (!tags.isEmpty()) { writer.name(JsonKeys.TAGS).value(logger, tags); } + if (!data.isEmpty()) { + writer.name(JsonKeys.DATA).value(logger, data); + } if (unknown != null) { for (String key : unknown.keySet()) { Object value = unknown.get(key); @@ -336,6 +349,7 @@ public static final class Deserializer implements JsonDeserializer SpanStatus status = null; String origin = null; Map tags = null; + Map data = null; Map unknown = null; while (reader.peek() == JsonToken.NAME) { @@ -367,6 +381,9 @@ public static final class Deserializer implements JsonDeserializer CollectionUtils.newConcurrentHashMap( (Map) reader.nextObjectOrNull()); break; + case JsonKeys.DATA: + data = (Map) reader.nextObjectOrNull(); + break; default: if (unknown == null) { unknown = new ConcurrentHashMap<>(); @@ -404,9 +421,15 @@ public static final class Deserializer implements JsonDeserializer spanContext.setDescription(description); spanContext.setStatus(status); spanContext.setOrigin(origin); + if (tags != null) { spanContext.tags = tags; } + + if (data != null) { + spanContext.data = data; + } + spanContext.setUnknown(unknown); reader.endObject(); return spanContext; diff --git a/sentry/src/main/java/io/sentry/protocol/SentryTransaction.java b/sentry/src/main/java/io/sentry/protocol/SentryTransaction.java index 2247925e182..78745252487 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryTransaction.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryTransaction.java @@ -77,8 +77,9 @@ public SentryTransaction(final @NotNull SentryTracer sentryTracer) { contexts.putAll(sentryTracer.getContexts()); final SpanContext tracerContext = sentryTracer.getSpanContext(); + Map data = sentryTracer.getData(); // tags must be placed on the root of the transaction instead of contexts.trace.tags - contexts.setTrace( + final SpanContext tracerContextToSend = new SpanContext( tracerContext.getTraceId(), tracerContext.getSpanId(), @@ -87,18 +88,20 @@ public SentryTransaction(final @NotNull SentryTracer sentryTracer) { tracerContext.getDescription(), tracerContext.getSamplingDecision(), tracerContext.getStatus(), - tracerContext.getOrigin())); + tracerContext.getOrigin()); + for (final Map.Entry tag : tracerContext.getTags().entrySet()) { this.setTag(tag.getKey(), tag.getValue()); } - final Map data = sentryTracer.getData(); if (data != null) { for (final Map.Entry tag : data.entrySet()) { - this.setExtra(tag.getKey(), tag.getValue()); + tracerContextToSend.setData(tag.getKey(), tag.getValue()); } } + contexts.setTrace(tracerContextToSend); + this.transactionInfo = new TransactionInfo(sentryTracer.getTransactionNameSource().apiName()); } diff --git a/sentry/src/test/java/io/sentry/JsonSerializerTest.kt b/sentry/src/test/java/io/sentry/JsonSerializerTest.kt index 470a440f1c2..4697c66c835 100644 --- a/sentry/src/test/java/io/sentry/JsonSerializerTest.kt +++ b/sentry/src/test/java/io/sentry/JsonSerializerTest.kt @@ -855,8 +855,6 @@ class JsonSerializerTest { assertNotNull(element["spans"] as List<*>) assertEquals("myValue", (element["tags"] as Map<*, *>)["myTag"] as String) - assertEquals("dataValue", (element["extra"] as Map<*, *>)["dataKey"] as String) - val jsonSpan = (element["spans"] as List<*>)[0] as Map<*, *> assertNotNull(jsonSpan["trace_id"]) assertNotNull(jsonSpan["span_id"]) @@ -867,6 +865,7 @@ class JsonSerializerTest { assertNotNull(jsonSpan["start_timestamp"]) val jsonTrace = (element["contexts"] as Map<*, *>)["trace"] as Map<*, *> + assertEquals("dataValue", (jsonTrace["data"] as Map<*, *>)["dataKey"] as String) assertNotNull(jsonTrace["trace_id"] as String) assertNotNull(jsonTrace["span_id"] as String) assertEquals("http", jsonTrace["op"] as String) @@ -887,7 +886,10 @@ class JsonSerializerTest { "trace_id": "b156a475de54423d9c1571df97ec7eb6", "span_id": "0a53026963414893", "op": "http", - "status": "ok" + "status": "ok", + "data": { + "transactionDataKey": "transactionDataValue" + } }, "custom": { "some-key": "some-value" @@ -928,6 +930,7 @@ class JsonSerializerTest { assertEquals("0a53026963414893", transaction.contexts.trace!!.spanId.toString()) assertEquals("http", transaction.contexts.trace!!.operation) assertNotNull(transaction.contexts["custom"]) + assertEquals("transactionDataValue", transaction.contexts.trace!!.data!!["transactionDataKey"]) assertEquals("some-value", (transaction.contexts["custom"] as Map<*, *>)["some-key"]) assertEquals("extraValue", transaction.getExtra("extraKey")) diff --git a/sentry/src/test/java/io/sentry/SentryTracerTest.kt b/sentry/src/test/java/io/sentry/SentryTracerTest.kt index a3a131ea977..3d5eca7f485 100644 --- a/sentry/src/test/java/io/sentry/SentryTracerTest.kt +++ b/sentry/src/test/java/io/sentry/SentryTracerTest.kt @@ -752,13 +752,13 @@ class SentryTracerTest { } @Test - fun `sets ITransaction data as extra in SentryTransaction`() { + fun `sets ITransaction data as tracecontext data in SentryTransaction`() { val transaction = fixture.getSut(samplingDecision = TracesSamplingDecision(true)) transaction.setData("key", "val") transaction.finish() verify(fixture.scopes).captureTransaction( check { - assertEquals("val", it.getExtra("key")) + assertEquals("val", it.contexts.trace?.data?.get("key")) }, anyOrNull(), anyOrNull(), diff --git a/sentry/src/test/java/io/sentry/protocol/SpanContextSerializationTest.kt b/sentry/src/test/java/io/sentry/protocol/SpanContextSerializationTest.kt index bd3bcd72c35..707daa78f10 100644 --- a/sentry/src/test/java/io/sentry/protocol/SpanContextSerializationTest.kt +++ b/sentry/src/test/java/io/sentry/protocol/SpanContextSerializationTest.kt @@ -35,6 +35,7 @@ class SpanContextSerializationTest { setTag("2a5fa3f5-7b87-487f-aaa5-84567aa73642", "4781d51a-c5af-47f2-a4ed-f030c9b3e194") setTag("29106d7d-7fa4-444f-9d34-b9d7510c69ab", "218c23ea-694a-497e-bf6d-e5f26f1ad7bd") setTag("ba9ce913-269f-4c03-882d-8ca5e6991b14", "35a74e90-8db8-4610-a411-872cbc1030ac") + setData("spanContextDataKey", "spanContextDataValue") } } private val fixture = Fixture() diff --git a/sentry/src/test/resources/json/contexts.json b/sentry/src/test/resources/json/contexts.json index 2bf48a60c1a..a6f35b31a66 100644 --- a/sentry/src/test/resources/json/contexts.json +++ b/sentry/src/test/resources/json/contexts.json @@ -120,6 +120,10 @@ "2a5fa3f5-7b87-487f-aaa5-84567aa73642": "4781d51a-c5af-47f2-a4ed-f030c9b3e194", "29106d7d-7fa4-444f-9d34-b9d7510c69ab": "218c23ea-694a-497e-bf6d-e5f26f1ad7bd", "ba9ce913-269f-4c03-882d-8ca5e6991b14": "35a74e90-8db8-4610-a411-872cbc1030ac" + }, + "data": + { + "spanContextDataKey": "spanContextDataValue" } } } diff --git a/sentry/src/test/resources/json/sentry_base_event.json b/sentry/src/test/resources/json/sentry_base_event.json index cdd93ff32a9..d2d1fd00881 100644 --- a/sentry/src/test/resources/json/sentry_base_event.json +++ b/sentry/src/test/resources/json/sentry_base_event.json @@ -123,6 +123,10 @@ "2a5fa3f5-7b87-487f-aaa5-84567aa73642": "4781d51a-c5af-47f2-a4ed-f030c9b3e194", "29106d7d-7fa4-444f-9d34-b9d7510c69ab": "218c23ea-694a-497e-bf6d-e5f26f1ad7bd", "ba9ce913-269f-4c03-882d-8ca5e6991b14": "35a74e90-8db8-4610-a411-872cbc1030ac" + }, + "data": + { + "spanContextDataKey": "spanContextDataValue" } } }, diff --git a/sentry/src/test/resources/json/sentry_base_event_with_null_extra.json b/sentry/src/test/resources/json/sentry_base_event_with_null_extra.json index 2904aa56979..4ce74eaf09e 100644 --- a/sentry/src/test/resources/json/sentry_base_event_with_null_extra.json +++ b/sentry/src/test/resources/json/sentry_base_event_with_null_extra.json @@ -123,6 +123,10 @@ "2a5fa3f5-7b87-487f-aaa5-84567aa73642": "4781d51a-c5af-47f2-a4ed-f030c9b3e194", "29106d7d-7fa4-444f-9d34-b9d7510c69ab": "218c23ea-694a-497e-bf6d-e5f26f1ad7bd", "ba9ce913-269f-4c03-882d-8ca5e6991b14": "35a74e90-8db8-4610-a411-872cbc1030ac" + }, + "data": + { + "spanContextDataKey": "spanContextDataValue" } } }, diff --git a/sentry/src/test/resources/json/sentry_event.json b/sentry/src/test/resources/json/sentry_event.json index 8046c51a4ac..6d421fc9936 100644 --- a/sentry/src/test/resources/json/sentry_event.json +++ b/sentry/src/test/resources/json/sentry_event.json @@ -258,6 +258,10 @@ "2a5fa3f5-7b87-487f-aaa5-84567aa73642": "4781d51a-c5af-47f2-a4ed-f030c9b3e194", "29106d7d-7fa4-444f-9d34-b9d7510c69ab": "218c23ea-694a-497e-bf6d-e5f26f1ad7bd", "ba9ce913-269f-4c03-882d-8ca5e6991b14": "35a74e90-8db8-4610-a411-872cbc1030ac" + }, + "data": + { + "spanContextDataKey": "spanContextDataValue" } } }, diff --git a/sentry/src/test/resources/json/sentry_replay_event.json b/sentry/src/test/resources/json/sentry_replay_event.json index 3849ad8f092..d3970bf5b00 100644 --- a/sentry/src/test/resources/json/sentry_replay_event.json +++ b/sentry/src/test/resources/json/sentry_replay_event.json @@ -141,6 +141,10 @@ "2a5fa3f5-7b87-487f-aaa5-84567aa73642": "4781d51a-c5af-47f2-a4ed-f030c9b3e194", "29106d7d-7fa4-444f-9d34-b9d7510c69ab": "218c23ea-694a-497e-bf6d-e5f26f1ad7bd", "ba9ce913-269f-4c03-882d-8ca5e6991b14": "35a74e90-8db8-4610-a411-872cbc1030ac" + }, + "data": + { + "spanContextDataKey": "spanContextDataValue" } } }, diff --git a/sentry/src/test/resources/json/sentry_transaction.json b/sentry/src/test/resources/json/sentry_transaction.json index 8bca3484f72..33080c9686e 100644 --- a/sentry/src/test/resources/json/sentry_transaction.json +++ b/sentry/src/test/resources/json/sentry_transaction.json @@ -180,6 +180,10 @@ "2a5fa3f5-7b87-487f-aaa5-84567aa73642": "4781d51a-c5af-47f2-a4ed-f030c9b3e194", "29106d7d-7fa4-444f-9d34-b9d7510c69ab": "218c23ea-694a-497e-bf6d-e5f26f1ad7bd", "ba9ce913-269f-4c03-882d-8ca5e6991b14": "35a74e90-8db8-4610-a411-872cbc1030ac" + }, + "data": + { + "spanContextDataKey": "spanContextDataValue" } } }, diff --git a/sentry/src/test/resources/json/sentry_transaction_legacy_date_format.json b/sentry/src/test/resources/json/sentry_transaction_legacy_date_format.json index 8964302f728..0d6ed5eb095 100644 --- a/sentry/src/test/resources/json/sentry_transaction_legacy_date_format.json +++ b/sentry/src/test/resources/json/sentry_transaction_legacy_date_format.json @@ -180,6 +180,10 @@ "2a5fa3f5-7b87-487f-aaa5-84567aa73642": "4781d51a-c5af-47f2-a4ed-f030c9b3e194", "29106d7d-7fa4-444f-9d34-b9d7510c69ab": "218c23ea-694a-497e-bf6d-e5f26f1ad7bd", "ba9ce913-269f-4c03-882d-8ca5e6991b14": "35a74e90-8db8-4610-a411-872cbc1030ac" + }, + "data": + { + "spanContextDataKey": "spanContextDataValue" } } }, diff --git a/sentry/src/test/resources/json/sentry_transaction_no_measurement_unit.json b/sentry/src/test/resources/json/sentry_transaction_no_measurement_unit.json index 326e9cb904a..2d965be1b9c 100644 --- a/sentry/src/test/resources/json/sentry_transaction_no_measurement_unit.json +++ b/sentry/src/test/resources/json/sentry_transaction_no_measurement_unit.json @@ -150,6 +150,10 @@ "2a5fa3f5-7b87-487f-aaa5-84567aa73642": "4781d51a-c5af-47f2-a4ed-f030c9b3e194", "29106d7d-7fa4-444f-9d34-b9d7510c69ab": "218c23ea-694a-497e-bf6d-e5f26f1ad7bd", "ba9ce913-269f-4c03-882d-8ca5e6991b14": "35a74e90-8db8-4610-a411-872cbc1030ac" + }, + "data": + { + "spanContextDataKey": "spanContextDataValue" } } }, diff --git a/sentry/src/test/resources/json/span_context.json b/sentry/src/test/resources/json/span_context.json index 4a6e08bcc2d..c55841a391b 100644 --- a/sentry/src/test/resources/json/span_context.json +++ b/sentry/src/test/resources/json/span_context.json @@ -11,5 +11,9 @@ "2a5fa3f5-7b87-487f-aaa5-84567aa73642": "4781d51a-c5af-47f2-a4ed-f030c9b3e194", "29106d7d-7fa4-444f-9d34-b9d7510c69ab": "218c23ea-694a-497e-bf6d-e5f26f1ad7bd", "ba9ce913-269f-4c03-882d-8ca5e6991b14": "35a74e90-8db8-4610-a411-872cbc1030ac" + }, + "data": + { + "spanContextDataKey": "spanContextDataValue" } }