diff --git a/CHANGELOG.md b/CHANGELOG.md index 21ac3f0b3e..49f43332c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Features - Add support for `feedback` envelope header item type ([#3687](https://github.com/getsentry/sentry-java/pull/3687)) +- Add breadcrumb.origin field ([#3727](https://github.com/getsentry/sentry-java/pull/3727)) ### Fixes diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 3cf11a434d..5f8b061acf 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -109,6 +109,7 @@ public final class io/sentry/Breadcrumb : io/sentry/JsonSerializable, io/sentry/ public fun getData (Ljava/lang/String;)Ljava/lang/Object; public fun getLevel ()Lio/sentry/SentryLevel; public fun getMessage ()Ljava/lang/String; + public fun getOrigin ()Ljava/lang/String; public fun getTimestamp ()Ljava/util/Date; public fun getType ()Ljava/lang/String; public fun getUnknown ()Ljava/util/Map; @@ -127,6 +128,7 @@ public final class io/sentry/Breadcrumb : io/sentry/JsonSerializable, io/sentry/ public fun setData (Ljava/lang/String;Ljava/lang/Object;)V public fun setLevel (Lio/sentry/SentryLevel;)V public fun setMessage (Ljava/lang/String;)V + public fun setOrigin (Ljava/lang/String;)V public fun setType (Ljava/lang/String;)V public fun setUnknown (Ljava/util/Map;)V public static fun transaction (Ljava/lang/String;)Lio/sentry/Breadcrumb; @@ -148,6 +150,7 @@ public final class io/sentry/Breadcrumb$JsonKeys { public static final field DATA Ljava/lang/String; public static final field LEVEL Ljava/lang/String; public static final field MESSAGE Ljava/lang/String; + public static final field ORIGIN Ljava/lang/String; public static final field TIMESTAMP Ljava/lang/String; public static final field TYPE Ljava/lang/String; public fun ()V diff --git a/sentry/src/main/java/io/sentry/Breadcrumb.java b/sentry/src/main/java/io/sentry/Breadcrumb.java index da1453bc68..954c57c36a 100644 --- a/sentry/src/main/java/io/sentry/Breadcrumb.java +++ b/sentry/src/main/java/io/sentry/Breadcrumb.java @@ -34,6 +34,12 @@ public final class Breadcrumb implements JsonUnknown, JsonSerializable { /** Dotted strings that indicate what the crumb is or where it comes from. */ private @Nullable String category; + /** + * Origin of the breadcrumb that is used to identify source of the breadcrumb. For example hybrid + * SDKs can identify native breadcrumbs from JS or Flutter. + */ + private @Nullable String origin; + /** The level of the event. */ private @Nullable SentryLevel level; @@ -54,6 +60,7 @@ public Breadcrumb(final @NotNull Date timestamp) { this.message = breadcrumb.message; this.type = breadcrumb.type; this.category = breadcrumb.category; + this.origin = breadcrumb.origin; final Map dataClone = CollectionUtils.newConcurrentHashMap(breadcrumb.data); if (dataClone != null) { this.data = dataClone; @@ -78,6 +85,7 @@ public static Breadcrumb fromMap( String type = null; @NotNull Map data = new ConcurrentHashMap<>(); String category = null; + String origin = null; SentryLevel level = null; Map unknown = null; @@ -116,6 +124,9 @@ public static Breadcrumb fromMap( case JsonKeys.CATEGORY: category = (value instanceof String) ? (String) value : null; break; + case JsonKeys.ORIGIN: + origin = (value instanceof String) ? (String) value : null; + break; case JsonKeys.LEVEL: String levelString = (value instanceof String) ? (String) value : null; if (levelString != null) { @@ -140,6 +151,7 @@ public static Breadcrumb fromMap( breadcrumb.type = type; breadcrumb.data = data; breadcrumb.category = category; + breadcrumb.origin = origin; breadcrumb.level = level; breadcrumb.setUnknown(unknown); @@ -610,6 +622,24 @@ public void setCategory(@Nullable String category) { this.category = category; } + /** + * Returns the origin + * + * @return the origin + */ + public @Nullable String getOrigin() { + return origin; + } + + /** + * Sets the origin + * + * @param origin the origin + */ + public void setOrigin(@Nullable String origin) { + this.origin = origin; + } + /** * Returns the SentryLevel * @@ -638,12 +668,13 @@ public boolean equals(Object o) { && Objects.equals(message, that.message) && Objects.equals(type, that.type) && Objects.equals(category, that.category) + && Objects.equals(origin, that.origin) && level == that.level; } @Override public int hashCode() { - return Objects.hash(timestamp, message, type, category, level); + return Objects.hash(timestamp, message, type, category, origin, level); } // region json @@ -665,6 +696,7 @@ public static final class JsonKeys { public static final String TYPE = "type"; public static final String DATA = "data"; public static final String CATEGORY = "category"; + public static final String ORIGIN = "origin"; public static final String LEVEL = "level"; } @@ -683,6 +715,9 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger if (category != null) { writer.name(JsonKeys.CATEGORY).value(category); } + if (origin != null) { + writer.name(JsonKeys.ORIGIN).value(origin); + } if (level != null) { writer.name(JsonKeys.LEVEL).value(logger, level); } @@ -707,6 +742,7 @@ public static final class Deserializer implements JsonDeserializer { String type = null; @NotNull Map data = new ConcurrentHashMap<>(); String category = null; + String origin = null; SentryLevel level = null; Map unknown = null; @@ -736,6 +772,9 @@ public static final class Deserializer implements JsonDeserializer { case JsonKeys.CATEGORY: category = reader.nextStringOrNull(); break; + case JsonKeys.ORIGIN: + origin = reader.nextStringOrNull(); + break; case JsonKeys.LEVEL: try { level = new SentryLevel.Deserializer().deserialize(reader, logger); @@ -757,6 +796,7 @@ public static final class Deserializer implements JsonDeserializer { breadcrumb.type = type; breadcrumb.data = data; breadcrumb.category = category; + breadcrumb.origin = origin; breadcrumb.level = level; breadcrumb.setUnknown(unknown); diff --git a/sentry/src/test/java/io/sentry/BreadcrumbTest.kt b/sentry/src/test/java/io/sentry/BreadcrumbTest.kt index 2c15a13abe..048d761799 100644 --- a/sentry/src/test/java/io/sentry/BreadcrumbTest.kt +++ b/sentry/src/test/java/io/sentry/BreadcrumbTest.kt @@ -21,6 +21,7 @@ class BreadcrumbTest { val level = SentryLevel.DEBUG breadcrumb.level = level breadcrumb.category = "category" + breadcrumb.origin = "origin" val clone = Breadcrumb(breadcrumb) @@ -44,6 +45,7 @@ class BreadcrumbTest { val level = SentryLevel.DEBUG breadcrumb.level = level breadcrumb.category = "category" + breadcrumb.origin = "origin" val clone = Breadcrumb(breadcrumb) @@ -53,6 +55,7 @@ class BreadcrumbTest { assertEquals("type", clone.type) assertEquals(SentryLevel.DEBUG, clone.level) assertEquals("category", clone.category) + assertEquals("origin", clone.origin) } @Test @@ -67,6 +70,7 @@ class BreadcrumbTest { val level = SentryLevel.DEBUG breadcrumb.level = level breadcrumb.category = "category" + breadcrumb.origin = "origin" val clone = Breadcrumb(breadcrumb) @@ -77,6 +81,7 @@ class BreadcrumbTest { breadcrumb.type = "newType" breadcrumb.level = SentryLevel.FATAL breadcrumb.category = "newCategory" + breadcrumb.origin = "newOrigin" assertEquals("message", clone.message) assertEquals("data", clone.data["data"]) @@ -86,6 +91,7 @@ class BreadcrumbTest { assertEquals("type", clone.type) assertEquals(SentryLevel.DEBUG, clone.level) assertEquals("category", clone.category) + assertEquals("origin", clone.origin) } @Test diff --git a/sentry/src/test/java/io/sentry/protocol/BreadcrumbSerializationTest.kt b/sentry/src/test/java/io/sentry/protocol/BreadcrumbSerializationTest.kt index fed8c4649d..0e43168897 100644 --- a/sentry/src/test/java/io/sentry/protocol/BreadcrumbSerializationTest.kt +++ b/sentry/src/test/java/io/sentry/protocol/BreadcrumbSerializationTest.kt @@ -28,6 +28,7 @@ class BreadcrumbSerializationTest { type = "ace57e2e-305e-4048-abf0-6c8538ea7bf4" setData("6607d106-d426-462b-af74-f29fce978e48", "149bb94a-1387-4484-90be-2df15d1322ab") category = "b6eea851-5ae5-40ed-8fdd-5e1a655a879c" + origin = "4d8085ef-22fc-49d5-801e-55d509fd1a1c" level = SentryLevel.DEBUG } } @@ -59,6 +60,7 @@ class BreadcrumbSerializationTest { "6607d106-d426-462b-af74-f29fce978e48" to "149bb94a-1387-4484-90be-2df15d1322ab" ), "category" to "b6eea851-5ae5-40ed-8fdd-5e1a655a879c", + "origin" to "4d8085ef-22fc-49d5-801e-55d509fd1a1c", "level" to "debug" ) val actual = Breadcrumb.fromMap(map, SentryOptions()) @@ -69,6 +71,7 @@ class BreadcrumbSerializationTest { assertEquals(expected.type, actual?.type) assertEquals(expected.data, actual?.data) assertEquals(expected.category, actual?.category) + assertEquals(expected.origin, actual?.origin) assertEquals(expected.level, actual?.level) } diff --git a/sentry/src/test/resources/json/breadcrumb.json b/sentry/src/test/resources/json/breadcrumb.json index 053d538fbe..a8873b1a2d 100644 --- a/sentry/src/test/resources/json/breadcrumb.json +++ b/sentry/src/test/resources/json/breadcrumb.json @@ -7,5 +7,6 @@ "6607d106-d426-462b-af74-f29fce978e48": "149bb94a-1387-4484-90be-2df15d1322ab" }, "category": "b6eea851-5ae5-40ed-8fdd-5e1a655a879c", + "origin": "4d8085ef-22fc-49d5-801e-55d509fd1a1c", "level": "debug" } diff --git a/sentry/src/test/resources/json/sentry_base_event.json b/sentry/src/test/resources/json/sentry_base_event.json index afaf040863..c889fe6bdc 100644 --- a/sentry/src/test/resources/json/sentry_base_event.json +++ b/sentry/src/test/resources/json/sentry_base_event.json @@ -207,6 +207,7 @@ "6607d106-d426-462b-af74-f29fce978e48": "149bb94a-1387-4484-90be-2df15d1322ab" }, "category": "b6eea851-5ae5-40ed-8fdd-5e1a655a879c", + "origin": "4d8085ef-22fc-49d5-801e-55d509fd1a1c", "level": "debug" } ], 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 018cc7aae7..02f3c1502a 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 @@ -207,6 +207,7 @@ "6607d106-d426-462b-af74-f29fce978e48": "149bb94a-1387-4484-90be-2df15d1322ab" }, "category": "b6eea851-5ae5-40ed-8fdd-5e1a655a879c", + "origin": "4d8085ef-22fc-49d5-801e-55d509fd1a1c", "level": "debug" } ], diff --git a/sentry/src/test/resources/json/sentry_event.json b/sentry/src/test/resources/json/sentry_event.json index 6d0b351ed4..7ae1ba107f 100644 --- a/sentry/src/test/resources/json/sentry_event.json +++ b/sentry/src/test/resources/json/sentry_event.json @@ -342,6 +342,7 @@ "6607d106-d426-462b-af74-f29fce978e48": "149bb94a-1387-4484-90be-2df15d1322ab" }, "category": "b6eea851-5ae5-40ed-8fdd-5e1a655a879c", + "origin": "4d8085ef-22fc-49d5-801e-55d509fd1a1c", "level": "debug" } ], diff --git a/sentry/src/test/resources/json/sentry_transaction.json b/sentry/src/test/resources/json/sentry_transaction.json index 944a0bfe92..7363dd35d6 100644 --- a/sentry/src/test/resources/json/sentry_transaction.json +++ b/sentry/src/test/resources/json/sentry_transaction.json @@ -290,6 +290,7 @@ "6607d106-d426-462b-af74-f29fce978e48": "149bb94a-1387-4484-90be-2df15d1322ab" }, "category": "b6eea851-5ae5-40ed-8fdd-5e1a655a879c", + "origin": "4d8085ef-22fc-49d5-801e-55d509fd1a1c", "level": "debug" } ], 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 789c4fe2a9..a0f8a675ae 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 @@ -290,6 +290,7 @@ "6607d106-d426-462b-af74-f29fce978e48": "149bb94a-1387-4484-90be-2df15d1322ab" }, "category": "b6eea851-5ae5-40ed-8fdd-5e1a655a879c", + "origin": "4d8085ef-22fc-49d5-801e-55d509fd1a1c", "level": "debug" } ], 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 330555c1ec..8ffb9a8f5d 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 @@ -257,6 +257,7 @@ "6607d106-d426-462b-af74-f29fce978e48": "149bb94a-1387-4484-90be-2df15d1322ab" }, "category": "b6eea851-5ae5-40ed-8fdd-5e1a655a879c", + "origin": "4d8085ef-22fc-49d5-801e-55d509fd1a1c", "level": "debug" } ],