From 22029c16186877a937540f9f05715392220acbff Mon Sep 17 00:00:00 2001 From: Lukas Bloder Date: Mon, 7 Oct 2024 10:52:12 +0200 Subject: [PATCH 01/24] lazy uuid generation for SentryId and SpanId --- sentry/api/sentry.api | 1 + sentry/src/main/java/io/sentry/SpanId.java | 31 ++++++++++-------- .../java/io/sentry/protocol/SentryId.java | 32 +++++++++++-------- .../main/java/io/sentry/util/StringUtils.java | 2 +- 4 files changed, 38 insertions(+), 28 deletions(-) diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index e7ec93fd20d..fabda503234 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -6374,6 +6374,7 @@ public final class io/sentry/util/SpanUtils { } public final class io/sentry/util/StringUtils { + public static final field PROPER_NIL_UUID Ljava/lang/String; public static fun byteCountToString (J)Ljava/lang/String; public static fun calculateStringHash (Ljava/lang/String;Lio/sentry/ILogger;)Ljava/lang/String; public static fun camelCase (Ljava/lang/String;)Ljava/lang/String; diff --git a/sentry/src/main/java/io/sentry/SpanId.java b/sentry/src/main/java/io/sentry/SpanId.java index 70608fb7cbb..7857919e023 100644 --- a/sentry/src/main/java/io/sentry/SpanId.java +++ b/sentry/src/main/java/io/sentry/SpanId.java @@ -1,26 +1,31 @@ package io.sentry; -import io.sentry.util.Objects; +import static io.sentry.util.StringUtils.PROPER_NIL_UUID; + +import io.sentry.util.LazyEvaluator; import io.sentry.util.StringUtils; import java.io.IOException; +import java.util.Objects; import java.util.UUID; import org.jetbrains.annotations.NotNull; public final class SpanId implements JsonSerializable { - public static final SpanId EMPTY_ID = new SpanId(new UUID(0, 0)); + public static final SpanId EMPTY_ID = new SpanId(PROPER_NIL_UUID); - private final @NotNull String value; + private final @NotNull LazyEvaluator lazyValue; public SpanId(final @NotNull String value) { - this.value = Objects.requireNonNull(value, "value is required"); + Objects.requireNonNull(value, "value is required"); + this.lazyValue = new LazyEvaluator<>(() -> value); } public SpanId() { - this(UUID.randomUUID()); - } - - private SpanId(final @NotNull UUID uuid) { - this(StringUtils.normalizeUUID(uuid.toString()).replace("-", "").substring(0, 16)); + this.lazyValue = + new LazyEvaluator<>( + () -> + StringUtils.normalizeUUID(UUID.randomUUID().toString()) + .replace("-", "") + .substring(0, 16)); } @Override @@ -28,17 +33,17 @@ public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SpanId spanId = (SpanId) o; - return value.equals(spanId.value); + return lazyValue.getValue().equals(spanId.lazyValue.getValue()); } @Override public int hashCode() { - return value.hashCode(); + return lazyValue.getValue().hashCode(); } @Override public String toString() { - return this.value; + return lazyValue.getValue(); } // JsonElementSerializer @@ -46,7 +51,7 @@ public String toString() { @Override public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger logger) throws IOException { - writer.value(value); + writer.value(lazyValue.getValue()); } // JsonElementDeserializer diff --git a/sentry/src/main/java/io/sentry/protocol/SentryId.java b/sentry/src/main/java/io/sentry/protocol/SentryId.java index 109655fdf2b..ad2bc45ef60 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryId.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryId.java @@ -5,6 +5,7 @@ import io.sentry.JsonSerializable; import io.sentry.ObjectReader; import io.sentry.ObjectWriter; +import io.sentry.util.LazyEvaluator; import io.sentry.util.StringUtils; import java.io.IOException; import java.util.UUID; @@ -12,28 +13,37 @@ import org.jetbrains.annotations.Nullable; public final class SentryId implements JsonSerializable { - private final @NotNull UUID uuid; public static final SentryId EMPTY_ID = new SentryId(new UUID(0, 0)); + private final @NotNull LazyEvaluator lazyValue; + public SentryId() { this((UUID) null); } public SentryId(@Nullable UUID uuid) { - if (uuid == null) { - uuid = UUID.randomUUID(); + if (uuid != null) { + this.lazyValue = new LazyEvaluator<>(() -> uuid); + } else { + this.lazyValue = new LazyEvaluator<>(UUID::randomUUID); } - this.uuid = uuid; } public SentryId(final @NotNull String sentryIdString) { - this.uuid = fromStringSentryId(StringUtils.normalizeUUID(sentryIdString)); + if (sentryIdString.length() != 32 && sentryIdString.length() != 36) { + throw new IllegalArgumentException( + "String representation of SentryId has either 32 (UUID no dashes) " + + "or 36 characters long (completed UUID). Received: " + + sentryIdString); + } + this.lazyValue = + new LazyEvaluator<>(() -> fromStringSentryId(StringUtils.normalizeUUID(sentryIdString))); } @Override public String toString() { - return StringUtils.normalizeUUID(uuid.toString()).replace("-", ""); + return StringUtils.normalizeUUID(lazyValue.getValue().toString()).replace("-", ""); } @Override @@ -41,12 +51,12 @@ public boolean equals(final @Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SentryId sentryId = (SentryId) o; - return uuid.compareTo(sentryId.uuid) == 0; + return lazyValue.getValue().compareTo(sentryId.lazyValue.getValue()) == 0; } @Override public int hashCode() { - return uuid.hashCode(); + return lazyValue.getValue().hashCode(); } private @NotNull UUID fromStringSentryId(@NotNull String sentryIdString) { @@ -60,12 +70,6 @@ public int hashCode() { .insert(23, "-") .toString(); } - if (sentryIdString.length() != 36) { - throw new IllegalArgumentException( - "String representation of SentryId has either 32 (UUID no dashes) " - + "or 36 characters long (completed UUID). Received: " - + sentryIdString); - } return UUID.fromString(sentryIdString); } diff --git a/sentry/src/main/java/io/sentry/util/StringUtils.java b/sentry/src/main/java/io/sentry/util/StringUtils.java index e4bf6185016..249940a638c 100644 --- a/sentry/src/main/java/io/sentry/util/StringUtils.java +++ b/sentry/src/main/java/io/sentry/util/StringUtils.java @@ -20,8 +20,8 @@ public final class StringUtils { private static final Charset UTF_8 = Charset.forName("UTF-8"); + public static final String PROPER_NIL_UUID = "00000000-0000-0000-0000-000000000000"; private static final String CORRUPTED_NIL_UUID = "0000-0000"; - private static final String PROPER_NIL_UUID = "00000000-0000-0000-0000-000000000000"; private static final @NotNull Pattern PATTERN_WORD_SNAKE_CASE = Pattern.compile("[\\W_]+"); private StringUtils() {} From f947f824a43c43fbeaa24fe8d78797ff92b7362d Mon Sep 17 00:00:00 2001 From: Lukas Bloder Date: Mon, 7 Oct 2024 10:59:17 +0200 Subject: [PATCH 02/24] add changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77de2d5898e..14cfe204713 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ - Use `` to ensure Sentry Android auto init is not easily overwritten - Attach request body for `application/x-www-form-urlencoded` requests in Spring ([#3731](https://github.com/getsentry/sentry-java/pull/3731)) - Previously request body was only attached for `application/json` requests +- Lazy uuid generation for SentryId and SpanId ([#3770](https://github.com/getsentry/sentry-java/pull/3770)) ### Fixes From e6df0ad59ec3c9cb84fa912ac8021ce1af3f22ec Mon Sep 17 00:00:00 2001 From: Lukas Bloder Date: Fri, 11 Oct 2024 13:07:32 +0200 Subject: [PATCH 03/24] add tests for lazy init, rework SentryId to cache string result --- .../java/io/sentry/protocol/SentryId.java | 35 +++++-------- .../java/io/sentry/protocol/SentryIdTest.kt | 43 ++++++++++++++++ .../java/io/sentry/protocol/SpanIdTest.kt | 51 +++++++++++++++++++ 3 files changed, 108 insertions(+), 21 deletions(-) create mode 100644 sentry/src/test/java/io/sentry/protocol/SpanIdTest.kt diff --git a/sentry/src/main/java/io/sentry/protocol/SentryId.java b/sentry/src/main/java/io/sentry/protocol/SentryId.java index ad2bc45ef60..d60f6f3dc83 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryId.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryId.java @@ -16,7 +16,7 @@ public final class SentryId implements JsonSerializable { public static final SentryId EMPTY_ID = new SentryId(new UUID(0, 0)); - private final @NotNull LazyEvaluator lazyValue; + private final @NotNull LazyEvaluator lazyStringValue; public SentryId() { this((UUID) null); @@ -24,26 +24,26 @@ public SentryId() { public SentryId(@Nullable UUID uuid) { if (uuid != null) { - this.lazyValue = new LazyEvaluator<>(() -> uuid); + this.lazyStringValue = new LazyEvaluator<>(() -> uuidToSentryIdString(uuid)); } else { - this.lazyValue = new LazyEvaluator<>(UUID::randomUUID); + this.lazyStringValue = new LazyEvaluator<>(() -> uuidToSentryIdString(UUID.randomUUID())); } } public SentryId(final @NotNull String sentryIdString) { - if (sentryIdString.length() != 32 && sentryIdString.length() != 36) { + String normalized = StringUtils.normalizeUUID(sentryIdString); + if (normalized.length() != 32 && normalized.length() != 36) { throw new IllegalArgumentException( "String representation of SentryId has either 32 (UUID no dashes) " + "or 36 characters long (completed UUID). Received: " + sentryIdString); } - this.lazyValue = - new LazyEvaluator<>(() -> fromStringSentryId(StringUtils.normalizeUUID(sentryIdString))); + this.lazyStringValue = new LazyEvaluator<>(() -> uuidStringToSentryIdString(normalized)); } @Override public String toString() { - return StringUtils.normalizeUUID(lazyValue.getValue().toString()).replace("-", ""); + return lazyStringValue.getValue(); } @Override @@ -51,27 +51,20 @@ public boolean equals(final @Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SentryId sentryId = (SentryId) o; - return lazyValue.getValue().compareTo(sentryId.lazyValue.getValue()) == 0; + return lazyStringValue.getValue().compareTo(sentryId.lazyStringValue.getValue()) == 0; } @Override public int hashCode() { - return lazyValue.getValue().hashCode(); + return lazyStringValue.getValue().hashCode(); } - private @NotNull UUID fromStringSentryId(@NotNull String sentryIdString) { - if (sentryIdString.length() == 32) { - // expected format, SentryId is a UUID without dashes - sentryIdString = - new StringBuilder(sentryIdString) - .insert(8, "-") - .insert(13, "-") - .insert(18, "-") - .insert(23, "-") - .toString(); - } + private @NotNull String uuidToSentryIdString(@NotNull UUID uuid) { + return uuidStringToSentryIdString(uuid.toString()); + } - return UUID.fromString(sentryIdString); + private @NotNull String uuidStringToSentryIdString(@NotNull String uuidString) { + return StringUtils.normalizeUUID(uuidString).replace("-", ""); } // JsonSerializable diff --git a/sentry/src/test/java/io/sentry/protocol/SentryIdTest.kt b/sentry/src/test/java/io/sentry/protocol/SentryIdTest.kt index 2aa24ec8598..f26f5911560 100644 --- a/sentry/src/test/java/io/sentry/protocol/SentryIdTest.kt +++ b/sentry/src/test/java/io/sentry/protocol/SentryIdTest.kt @@ -1,5 +1,11 @@ package io.sentry.protocol +import io.sentry.util.StringUtils +import org.mockito.Mockito +import org.mockito.kotlin.any +import org.mockito.kotlin.never +import org.mockito.kotlin.times +import java.util.* import kotlin.test.Test import kotlin.test.assertEquals @@ -10,4 +16,41 @@ class SentryIdTest { val id = SentryId("0000-0000") assertEquals("00000000000000000000000000000000", id.toString()) } + + @Test + fun `UUID is not generated on initialization`() { + val uuid = UUID.randomUUID() + Mockito.mockStatic(UUID::class.java).use { utils -> + utils.`when` { UUID.randomUUID() }.thenReturn(uuid) + val ignored = SentryId() + utils.verify({ UUID.randomUUID() }, never()) + } + } + + @Test + fun `UUID is generated only once`() { + val uuid = UUID.randomUUID() + Mockito.mockStatic(UUID::class.java).use { utils -> + utils.`when` { UUID.randomUUID() }.thenReturn(uuid) + val sentryId = SentryId() + val uuid1 = sentryId.toString() + val uuid2 = sentryId.toString() + + assertEquals(uuid1, uuid2) + utils.verify({ UUID.randomUUID() }, times(1)) + } + } + + @Test + fun `normalizeUUID is only called once`() { + Mockito.mockStatic(StringUtils::class.java).use { utils -> + utils.`when` { StringUtils.normalizeUUID(any()) }.thenReturn("00000000000000000000000000000000") + val sentryId = SentryId() + val uuid1 = sentryId.toString() + val uuid2 = sentryId.toString() + + assertEquals(uuid1, uuid2) + utils.verify({ StringUtils.normalizeUUID(any()) }, times(1)) + } + } } diff --git a/sentry/src/test/java/io/sentry/protocol/SpanIdTest.kt b/sentry/src/test/java/io/sentry/protocol/SpanIdTest.kt new file mode 100644 index 00000000000..071d402e2fa --- /dev/null +++ b/sentry/src/test/java/io/sentry/protocol/SpanIdTest.kt @@ -0,0 +1,51 @@ +package io.sentry.protocol + +import io.sentry.SpanId +import io.sentry.util.StringUtils +import org.mockito.Mockito +import org.mockito.kotlin.any +import org.mockito.kotlin.never +import org.mockito.kotlin.times +import java.util.* +import kotlin.test.Test +import kotlin.test.assertEquals + +class SpanIdTest { + + @Test + fun `UUID is not generated on initialization`() { + val uuid = UUID.randomUUID() + Mockito.mockStatic(UUID::class.java).use { utils -> + utils.`when` { UUID.randomUUID() }.thenReturn(uuid) + val ignored = SpanId() + utils.verify({ UUID.randomUUID() }, never()) + } + } + + @Test + fun `UUID is generated only once`() { + val uuid = UUID.randomUUID() + Mockito.mockStatic(java.util.UUID::class.java).use { utils -> + utils.`when` { UUID.randomUUID() }.thenReturn(uuid) + val spanId = SpanId() + val uuid1 = spanId.toString() + val uuid2 = spanId.toString() + + assertEquals(uuid1, uuid2) + utils.verify({ UUID.randomUUID() }, times(1)) + } + } + + @Test + fun `normalizeUUID is only called once`() { + Mockito.mockStatic(StringUtils::class.java).use { utils -> + utils.`when` { StringUtils.normalizeUUID(any()) }.thenReturn("00000000000000000000000000000000") + val spanId = SpanId() + val uuid1 = spanId.toString() + val uuid2 = spanId.toString() + + assertEquals(uuid1, uuid2) + utils.verify({ StringUtils.normalizeUUID(any()) }, times(1)) + } + } +} From f2fba851635cf2a70e1c2380e58a0d17bd6e7b3f Mon Sep 17 00:00:00 2001 From: Lukas Bloder Date: Fri, 18 Oct 2024 09:26:53 +0200 Subject: [PATCH 04/24] fix changelog --- CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b106f876a29..b029c20a08c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +## Features + +- Lazy uuid generation for SentryId and SpanId ([#3770](https://github.com/getsentry/sentry-java/pull/3770)) + ## 8.0.0-beta.1 ### Breaking Changes @@ -45,7 +51,6 @@ - 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 -- Lazy uuid generation for SentryId and SpanId ([#3770](https://github.com/getsentry/sentry-java/pull/3770)) ### Fixes From c7ab29492a1346b2ba66f89106bbe9bd8e837f18 Mon Sep 17 00:00:00 2001 From: Lukas Bloder Date: Tue, 22 Oct 2024 15:42:47 +0200 Subject: [PATCH 05/24] Update sentry/src/main/java/io/sentry/protocol/SentryId.java Co-authored-by: Stefano --- sentry/src/main/java/io/sentry/protocol/SentryId.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry/src/main/java/io/sentry/protocol/SentryId.java b/sentry/src/main/java/io/sentry/protocol/SentryId.java index d60f6f3dc83..ceecefbb7cb 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryId.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryId.java @@ -51,7 +51,7 @@ public boolean equals(final @Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SentryId sentryId = (SentryId) o; - return lazyStringValue.getValue().compareTo(sentryId.lazyStringValue.getValue()) == 0; + return lazyStringValue.getValue().equals(sentryId.lazyStringValue.getValue()); } @Override From f1e66b2a892c78e960d5d2560d308fdc028cabe5 Mon Sep 17 00:00:00 2001 From: Lukas Bloder Date: Tue, 22 Oct 2024 15:42:57 +0200 Subject: [PATCH 06/24] Update sentry/src/main/java/io/sentry/protocol/SentryId.java Co-authored-by: Stefano --- sentry/src/main/java/io/sentry/protocol/SentryId.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sentry/src/main/java/io/sentry/protocol/SentryId.java b/sentry/src/main/java/io/sentry/protocol/SentryId.java index ceecefbb7cb..da73df2077a 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryId.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryId.java @@ -31,14 +31,14 @@ public SentryId(@Nullable UUID uuid) { } public SentryId(final @NotNull String sentryIdString) { - String normalized = StringUtils.normalizeUUID(sentryIdString); + final @NotNull String normalized = StringUtils.normalizeUUID(sentryIdString); if (normalized.length() != 32 && normalized.length() != 36) { throw new IllegalArgumentException( "String representation of SentryId has either 32 (UUID no dashes) " + "or 36 characters long (completed UUID). Received: " + sentryIdString); } - this.lazyStringValue = new LazyEvaluator<>(() -> uuidStringToSentryIdString(normalized)); + this.lazyStringValue = new LazyEvaluator<>(() -> normalize(normalized)); } @Override From 76b23c8c760f74270be81fe791f163eb14c889ed Mon Sep 17 00:00:00 2001 From: Lukas Bloder Date: Tue, 22 Oct 2024 15:43:04 +0200 Subject: [PATCH 07/24] Update sentry/src/main/java/io/sentry/protocol/SentryId.java Co-authored-by: Stefano --- sentry/src/main/java/io/sentry/protocol/SentryId.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry/src/main/java/io/sentry/protocol/SentryId.java b/sentry/src/main/java/io/sentry/protocol/SentryId.java index da73df2077a..2d59579894f 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryId.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryId.java @@ -63,7 +63,7 @@ public int hashCode() { return uuidStringToSentryIdString(uuid.toString()); } - private @NotNull String uuidStringToSentryIdString(@NotNull String uuidString) { + private @NotNull String normalize(@NotNull String uuidString) { return StringUtils.normalizeUUID(uuidString).replace("-", ""); } From 8599535563fab21e5f5e2c88310a208a810d6a81 Mon Sep 17 00:00:00 2001 From: Lukas Bloder Date: Tue, 22 Oct 2024 15:43:11 +0200 Subject: [PATCH 08/24] Update sentry/src/main/java/io/sentry/protocol/SentryId.java Co-authored-by: Stefano --- sentry/src/main/java/io/sentry/protocol/SentryId.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/sentry/src/main/java/io/sentry/protocol/SentryId.java b/sentry/src/main/java/io/sentry/protocol/SentryId.java index 2d59579894f..d105cd9d07e 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryId.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryId.java @@ -59,9 +59,6 @@ public int hashCode() { return lazyStringValue.getValue().hashCode(); } - private @NotNull String uuidToSentryIdString(@NotNull UUID uuid) { - return uuidStringToSentryIdString(uuid.toString()); - } private @NotNull String normalize(@NotNull String uuidString) { return StringUtils.normalizeUUID(uuidString).replace("-", ""); From 3bae1d766728ffa6d1e92278433968e3dfe2d927 Mon Sep 17 00:00:00 2001 From: Lukas Bloder Date: Tue, 22 Oct 2024 15:43:21 +0200 Subject: [PATCH 09/24] Update sentry/src/main/java/io/sentry/protocol/SentryId.java Co-authored-by: Stefano --- sentry/src/main/java/io/sentry/protocol/SentryId.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sentry/src/main/java/io/sentry/protocol/SentryId.java b/sentry/src/main/java/io/sentry/protocol/SentryId.java index d105cd9d07e..05734cf40d5 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryId.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryId.java @@ -24,9 +24,9 @@ public SentryId() { public SentryId(@Nullable UUID uuid) { if (uuid != null) { - this.lazyStringValue = new LazyEvaluator<>(() -> uuidToSentryIdString(uuid)); + this.lazyStringValue = new LazyEvaluator<>(() -> normalize(uuid.toString())); } else { - this.lazyStringValue = new LazyEvaluator<>(() -> uuidToSentryIdString(UUID.randomUUID())); + this.lazyStringValue = new LazyEvaluator<>(() -> normalize(UUID.randomUUID().toString())); } } From edba0c7914d6e325a79fe33992443f4c51cc0ba2 Mon Sep 17 00:00:00 2001 From: Sentry Github Bot Date: Tue, 22 Oct 2024 13:50:43 +0000 Subject: [PATCH 10/24] Format code --- sentry/src/main/java/io/sentry/protocol/SentryId.java | 1 - 1 file changed, 1 deletion(-) diff --git a/sentry/src/main/java/io/sentry/protocol/SentryId.java b/sentry/src/main/java/io/sentry/protocol/SentryId.java index 05734cf40d5..bf1bde0c4dc 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryId.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryId.java @@ -59,7 +59,6 @@ public int hashCode() { return lazyStringValue.getValue().hashCode(); } - private @NotNull String normalize(@NotNull String uuidString) { return StringUtils.normalizeUUID(uuidString).replace("-", ""); } From c18a74d560b57bce8321ad07d509942bf5882f88 Mon Sep 17 00:00:00 2001 From: Lukas Bloder Date: Tue, 22 Oct 2024 15:50:58 +0200 Subject: [PATCH 11/24] Add object comparison to SpanFrameMetricsCollector only check for time difference of .equals if objects are not the same --- .../android/core/SpanFrameMetricsCollector.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SpanFrameMetricsCollector.java b/sentry-android-core/src/main/java/io/sentry/android/core/SpanFrameMetricsCollector.java index 18427dd5890..d4e47ddc806 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SpanFrameMetricsCollector.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SpanFrameMetricsCollector.java @@ -45,17 +45,19 @@ public class SpanFrameMetricsCollector private final @NotNull SortedSet runningSpans = new TreeSet<>( (o1, o2) -> { + if (o1 == o2) { + return 0; + } int timeDiff = o1.getStartDate().compareTo(o2.getStartDate()); if (timeDiff != 0) { return timeDiff; - } else { - // TreeSet uses compareTo to check for duplicates, so ensure that - // two non-equal spans with the same start date are not considered equal - return o1.getSpanContext() - .getSpanId() - .toString() - .compareTo(o2.getSpanContext().getSpanId().toString()); } + // TreeSet uses compareTo to check for duplicates, so ensure that + // two non-equal spans with the same start date are not considered equal + return o1.getSpanContext() + .getSpanId() + .toString() + .compareTo(o2.getSpanContext().getSpanId().toString()); }); // all collected frames, sorted by frame end time From a50d8817909247ed4049e4451df465c78ce9af67 Mon Sep 17 00:00:00 2001 From: Lukas Bloder Date: Tue, 22 Oct 2024 16:17:20 +0200 Subject: [PATCH 12/24] add proposed SentryUUID class --- .../src/main/java/io/sentry/SentryUUID.java | 306 ++++++++++++++++++ 1 file changed, 306 insertions(+) create mode 100644 sentry/src/main/java/io/sentry/SentryUUID.java diff --git a/sentry/src/main/java/io/sentry/SentryUUID.java b/sentry/src/main/java/io/sentry/SentryUUID.java new file mode 100644 index 00000000000..9dc9719a9fb --- /dev/null +++ b/sentry/src/main/java/io/sentry/SentryUUID.java @@ -0,0 +1,306 @@ +package io.sentry; + +import java.util.Arrays; +import java.util.Random; +import java.util.UUID; + +/** + * Utility class for faster id generation for SentryId, SpanId, and unique filenames uses throughout the SDK + * It uses our vendored Random class instead of SecureRandom for improved performance + * It directly creates a correctly formatted String to be used as IDs in the Sentry context. + * + * Id generation is sped up by 4 to 10 times based on the underlying java version. + * + * Based on the work of Jon Chambers + * Here: https://github.com/jchambers/fast-uuid + */ +public class SentryUUID { + + private static final int UUID_STRING_LENGTH = 36; + private static final int SENTRY_UUID_STRING_LENGTH = 32; + private static final int SENTRY_SPAN_UUID_STRING_LENGTH = 16; + + private static final char[] HEX_DIGITS = + new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + private static final long[] HEX_VALUES = new long[128]; + + static { + Arrays.fill(HEX_VALUES, -1); + + HEX_VALUES['0'] = 0x0; + HEX_VALUES['1'] = 0x1; + HEX_VALUES['2'] = 0x2; + HEX_VALUES['3'] = 0x3; + HEX_VALUES['4'] = 0x4; + HEX_VALUES['5'] = 0x5; + HEX_VALUES['6'] = 0x6; + HEX_VALUES['7'] = 0x7; + HEX_VALUES['8'] = 0x8; + HEX_VALUES['9'] = 0x9; + + HEX_VALUES['a'] = 0xa; + HEX_VALUES['b'] = 0xb; + HEX_VALUES['c'] = 0xc; + HEX_VALUES['d'] = 0xd; + HEX_VALUES['e'] = 0xe; + HEX_VALUES['f'] = 0xf; + + HEX_VALUES['A'] = 0xa; + HEX_VALUES['B'] = 0xb; + HEX_VALUES['C'] = 0xc; + HEX_VALUES['D'] = 0xd; + HEX_VALUES['E'] = 0xe; + HEX_VALUES['F'] = 0xf; + } + + private SentryUUID() { + // A private constructor prevents callers from accidentally instantiating FastUUID objects + } + + /** + * Parses a UUID from the given character sequence. The character sequence must represent a UUID as described in + * {@link UUID#toString()}. + * + * @param uuidSequence the character sequence from which to parse a UUID + * + * @return the UUID represented by the given character sequence + * + * @throws IllegalArgumentException if the given character sequence does not conform to the string representation as + * described in {@link UUID#toString()} + */ + public static UUID parseUUID(final CharSequence uuidSequence) { + if (uuidSequence.length() != UUID_STRING_LENGTH || + uuidSequence.charAt(8) != '-' || + uuidSequence.charAt(13) != '-' || + uuidSequence.charAt(18) != '-' || + uuidSequence.charAt(23) != '-') { + + throw new IllegalArgumentException("Illegal UUID string: " + uuidSequence); + } + + long mostSignificantBits = getHexValueForChar(uuidSequence.charAt(0)) << 60; + mostSignificantBits |= getHexValueForChar(uuidSequence.charAt(1)) << 56; + mostSignificantBits |= getHexValueForChar(uuidSequence.charAt(2)) << 52; + mostSignificantBits |= getHexValueForChar(uuidSequence.charAt(3)) << 48; + mostSignificantBits |= getHexValueForChar(uuidSequence.charAt(4)) << 44; + mostSignificantBits |= getHexValueForChar(uuidSequence.charAt(5)) << 40; + mostSignificantBits |= getHexValueForChar(uuidSequence.charAt(6)) << 36; + mostSignificantBits |= getHexValueForChar(uuidSequence.charAt(7)) << 32; + + mostSignificantBits |= getHexValueForChar(uuidSequence.charAt(9)) << 28; + mostSignificantBits |= getHexValueForChar(uuidSequence.charAt(10)) << 24; + mostSignificantBits |= getHexValueForChar(uuidSequence.charAt(11)) << 20; + mostSignificantBits |= getHexValueForChar(uuidSequence.charAt(12)) << 16; + + mostSignificantBits |= getHexValueForChar(uuidSequence.charAt(14)) << 12; + mostSignificantBits |= getHexValueForChar(uuidSequence.charAt(15)) << 8; + mostSignificantBits |= getHexValueForChar(uuidSequence.charAt(16)) << 4; + mostSignificantBits |= getHexValueForChar(uuidSequence.charAt(17)); + + long leastSignificantBits = getHexValueForChar(uuidSequence.charAt(19)) << 60; + leastSignificantBits |= getHexValueForChar(uuidSequence.charAt(20)) << 56; + leastSignificantBits |= getHexValueForChar(uuidSequence.charAt(21)) << 52; + leastSignificantBits |= getHexValueForChar(uuidSequence.charAt(22)) << 48; + + leastSignificantBits |= getHexValueForChar(uuidSequence.charAt(24)) << 44; + leastSignificantBits |= getHexValueForChar(uuidSequence.charAt(25)) << 40; + leastSignificantBits |= getHexValueForChar(uuidSequence.charAt(26)) << 36; + leastSignificantBits |= getHexValueForChar(uuidSequence.charAt(27)) << 32; + leastSignificantBits |= getHexValueForChar(uuidSequence.charAt(28)) << 28; + leastSignificantBits |= getHexValueForChar(uuidSequence.charAt(29)) << 24; + leastSignificantBits |= getHexValueForChar(uuidSequence.charAt(30)) << 20; + leastSignificantBits |= getHexValueForChar(uuidSequence.charAt(31)) << 16; + leastSignificantBits |= getHexValueForChar(uuidSequence.charAt(32)) << 12; + leastSignificantBits |= getHexValueForChar(uuidSequence.charAt(33)) << 8; + leastSignificantBits |= getHexValueForChar(uuidSequence.charAt(34)) << 4; + leastSignificantBits |= getHexValueForChar(uuidSequence.charAt(35)); + + return new UUID(mostSignificantBits, leastSignificantBits); + } + + public static String generateSentryId() { + return toSentryIdString(SentryUUID.randomUUID()); + } + + public static String generateSpanId() { + return toSentrySpanIdString(SentryUUID.randomHalfLengthUUID()); + } + + /** + * Returns a string representation of the given UUID. The returned string is formatted as described in + * {@link UUID#toString()}. + * + * @param uuid the UUID to represent as a string + * + * @return a string representation of the given UUID + */ + public static String toString(final UUID uuid) { + + final long mostSignificantBits = uuid.getMostSignificantBits(); + final long leastSignificantBits = uuid.getLeastSignificantBits(); + + final char[] uuidChars = new char[UUID_STRING_LENGTH]; + + uuidChars[0] = HEX_DIGITS[(int) ((mostSignificantBits & 0xf000000000000000L) >>> 60)]; + uuidChars[1] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0f00000000000000L) >>> 56)]; + uuidChars[2] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00f0000000000000L) >>> 52)]; + uuidChars[3] = HEX_DIGITS[(int) ((mostSignificantBits & 0x000f000000000000L) >>> 48)]; + uuidChars[4] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0000f00000000000L) >>> 44)]; + uuidChars[5] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00000f0000000000L) >>> 40)]; + uuidChars[6] = HEX_DIGITS[(int) ((mostSignificantBits & 0x000000f000000000L) >>> 36)]; + uuidChars[7] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0000000f00000000L) >>> 32)]; + uuidChars[8] = '-'; + uuidChars[9] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00000000f0000000L) >>> 28)]; + uuidChars[10] = HEX_DIGITS[(int) ((mostSignificantBits & 0x000000000f000000L) >>> 24)]; + uuidChars[11] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0000000000f00000L) >>> 20)]; + uuidChars[12] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00000000000f0000L) >>> 16)]; + uuidChars[13] = '-'; + uuidChars[14] = HEX_DIGITS[(int) ((mostSignificantBits & 0x000000000000f000L) >>> 12)]; + uuidChars[15] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0000000000000f00L) >>> 8)]; + uuidChars[16] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00000000000000f0L) >>> 4)]; + uuidChars[17] = HEX_DIGITS[(int) (mostSignificantBits & 0x000000000000000fL)]; + uuidChars[18] = '-'; + uuidChars[19] = HEX_DIGITS[(int) ((leastSignificantBits & 0xf000000000000000L) >>> 60)]; + uuidChars[20] = HEX_DIGITS[(int) ((leastSignificantBits & 0x0f00000000000000L) >>> 56)]; + uuidChars[21] = HEX_DIGITS[(int) ((leastSignificantBits & 0x00f0000000000000L) >>> 52)]; + uuidChars[22] = HEX_DIGITS[(int) ((leastSignificantBits & 0x000f000000000000L) >>> 48)]; + uuidChars[23] = '-'; + uuidChars[24] = HEX_DIGITS[(int) ((leastSignificantBits & 0x0000f00000000000L) >>> 44)]; + uuidChars[25] = HEX_DIGITS[(int) ((leastSignificantBits & 0x00000f0000000000L) >>> 40)]; + uuidChars[26] = HEX_DIGITS[(int) ((leastSignificantBits & 0x000000f000000000L) >>> 36)]; + uuidChars[27] = HEX_DIGITS[(int) ((leastSignificantBits & 0x0000000f00000000L) >>> 32)]; + uuidChars[28] = HEX_DIGITS[(int) ((leastSignificantBits & 0x00000000f0000000L) >>> 28)]; + uuidChars[29] = HEX_DIGITS[(int) ((leastSignificantBits & 0x000000000f000000L) >>> 24)]; + uuidChars[30] = HEX_DIGITS[(int) ((leastSignificantBits & 0x0000000000f00000L) >>> 20)]; + uuidChars[31] = HEX_DIGITS[(int) ((leastSignificantBits & 0x00000000000f0000L) >>> 16)]; + uuidChars[32] = HEX_DIGITS[(int) ((leastSignificantBits & 0x000000000000f000L) >>> 12)]; + uuidChars[33] = HEX_DIGITS[(int) ((leastSignificantBits & 0x0000000000000f00L) >>> 8)]; + uuidChars[34] = HEX_DIGITS[(int) ((leastSignificantBits & 0x00000000000000f0L) >>> 4)]; + uuidChars[35] = HEX_DIGITS[(int) (leastSignificantBits & 0x000000000000000fL)]; + + return new String(uuidChars); + } + + public static String toSentryIdString(final UUID uuid) { + + final long mostSignificantBits = uuid.getMostSignificantBits(); + final long leastSignificantBits = uuid.getLeastSignificantBits(); + + return toSentryIdString(mostSignificantBits, leastSignificantBits); + } + + public static String toSentryIdString(long mostSignificantBits, long leastSignificantBits) { + final char[] uuidChars = new char[SENTRY_UUID_STRING_LENGTH]; + + fillMostSignificantBits(uuidChars, mostSignificantBits); + + uuidChars[16] = HEX_DIGITS[(int) ((leastSignificantBits & 0xf000000000000000L) >>> 60)]; + uuidChars[17] = HEX_DIGITS[(int) ((leastSignificantBits & 0x0f00000000000000L) >>> 56)]; + uuidChars[18] = HEX_DIGITS[(int) ((leastSignificantBits & 0x00f0000000000000L) >>> 52)]; + uuidChars[19] = HEX_DIGITS[(int) ((leastSignificantBits & 0x000f000000000000L) >>> 48)]; + uuidChars[20] = HEX_DIGITS[(int) ((leastSignificantBits & 0x0000f00000000000L) >>> 44)]; + uuidChars[21] = HEX_DIGITS[(int) ((leastSignificantBits & 0x00000f0000000000L) >>> 40)]; + uuidChars[22] = HEX_DIGITS[(int) ((leastSignificantBits & 0x000000f000000000L) >>> 36)]; + uuidChars[23] = HEX_DIGITS[(int) ((leastSignificantBits & 0x0000000f00000000L) >>> 32)]; + uuidChars[24] = HEX_DIGITS[(int) ((leastSignificantBits & 0x00000000f0000000L) >>> 28)]; + uuidChars[25] = HEX_DIGITS[(int) ((leastSignificantBits & 0x000000000f000000L) >>> 24)]; + uuidChars[26] = HEX_DIGITS[(int) ((leastSignificantBits & 0x0000000000f00000L) >>> 20)]; + uuidChars[27] = HEX_DIGITS[(int) ((leastSignificantBits & 0x00000000000f0000L) >>> 16)]; + uuidChars[28] = HEX_DIGITS[(int) ((leastSignificantBits & 0x000000000000f000L) >>> 12)]; + uuidChars[29] = HEX_DIGITS[(int) ((leastSignificantBits & 0x0000000000000f00L) >>> 8)]; + uuidChars[30] = HEX_DIGITS[(int) ((leastSignificantBits & 0x00000000000000f0L) >>> 4)]; + uuidChars[31] = HEX_DIGITS[(int) (leastSignificantBits & 0x000000000000000fL)]; + + return new String(uuidChars); + } + + public static String toSentrySpanIdString(final UUID uuid) { + + final long mostSignificantBits = uuid.getMostSignificantBits(); + return toSentrySpanIdString(mostSignificantBits); + + } + + public static String toSentrySpanIdString(long mostSignificantBits) { + final char[] uuidChars = new char[SENTRY_SPAN_UUID_STRING_LENGTH]; + + fillMostSignificantBits(uuidChars, mostSignificantBits); + + return new String(uuidChars); + } + + private static void fillMostSignificantBits(final char[] uuidChars, final long mostSignificantBits) { + uuidChars[0] = HEX_DIGITS[(int) ((mostSignificantBits & 0xf000000000000000L) >>> 60)]; + uuidChars[1] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0f00000000000000L) >>> 56)]; + uuidChars[2] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00f0000000000000L) >>> 52)]; + uuidChars[3] = HEX_DIGITS[(int) ((mostSignificantBits & 0x000f000000000000L) >>> 48)]; + uuidChars[4] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0000f00000000000L) >>> 44)]; + uuidChars[5] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00000f0000000000L) >>> 40)]; + uuidChars[6] = HEX_DIGITS[(int) ((mostSignificantBits & 0x000000f000000000L) >>> 36)]; + uuidChars[7] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0000000f00000000L) >>> 32)]; + uuidChars[8] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00000000f0000000L) >>> 28)]; + uuidChars[9] = HEX_DIGITS[(int) ((mostSignificantBits & 0x000000000f000000L) >>> 24)]; + uuidChars[10] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0000000000f00000L) >>> 20)]; + uuidChars[11] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00000000000f0000L) >>> 16)]; + uuidChars[12] = HEX_DIGITS[(int) ((mostSignificantBits & 0x000000000000f000L) >>> 12)]; + uuidChars[13] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0000000000000f00L) >>> 8)]; + uuidChars[14] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00000000000000f0L) >>> 4)]; + uuidChars[15] = HEX_DIGITS[(int) (mostSignificantBits & 0x000000000000000fL)]; + } + + static long getHexValueForChar(final char c) { + try { + if (HEX_VALUES[c] < 0) { + throw new IllegalArgumentException("Illegal hexadecimal digit: " + c); + } + } catch (final ArrayIndexOutOfBoundsException e) { + throw new IllegalArgumentException("Illegal hexadecimal digit: " + c); + } + + return HEX_VALUES[c]; + } + + public static long randomHalfLengthUUID() { + Random random = SentryUUID.Holder.numberGenerator; + byte[] randomBytes = new byte[8]; + random.nextBytes(randomBytes); + randomBytes[6] &= 0x0f; /* clear version */ + randomBytes[6] |= 0x40; /* set to version 4 */ + + long msb = 0; + + for (int i=0; i<8; i++) + msb = (msb << 8) | (randomBytes[i] & 0xff); + + return msb; + } + + public static UUID randomUUID() { + Random random = SentryUUID.Holder.numberGenerator; + byte[] randomBytes = new byte[16]; + random.nextBytes(randomBytes); + randomBytes[6] &= 0x0f; /* clear version */ + randomBytes[6] |= 0x40; /* set to version 4 */ + randomBytes[8] &= 0x3f; /* clear variant */ + randomBytes[8] |= (byte) 0x80; /* set to IETF variant */ + + long msb = 0; + long lsb = 0; + + for (int i=0; i<8; i++) + msb = (msb << 8) | (randomBytes[i] & 0xff); + + for (int i=8; i<16; i++) + lsb = (lsb << 8) | (randomBytes[i] & 0xff); + + return new UUID(msb, lsb); + } + + private static class Holder { + static final Random numberGenerator = new Random(); + + private Holder() { + } + } +} From 149d59cd3f6bfb65a8992b4444d014cfa2ed3dbe Mon Sep 17 00:00:00 2001 From: Lukas Bloder Date: Tue, 22 Oct 2024 16:17:49 +0200 Subject: [PATCH 13/24] [WIP] use SentryUUID instead of UUID.random().toString --- .../java/io/sentry/android/core/AndroidProfiler.java | 3 ++- .../main/java/io/sentry/android/core/Installation.java | 3 ++- .../internal/util/SentryFrameMetricsCollector.java | 3 ++- sentry/src/main/java/io/sentry/ProfilingTraceData.java | 2 +- sentry/src/main/java/io/sentry/SpanId.java | 10 ++-------- .../src/main/java/io/sentry/cache/EnvelopeCache.java | 3 ++- sentry/src/main/java/io/sentry/protocol/SentryId.java | 5 +++-- 7 files changed, 14 insertions(+), 15 deletions(-) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidProfiler.java b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidProfiler.java index 379123c2c85..03d0501926a 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidProfiler.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidProfiler.java @@ -13,6 +13,7 @@ import io.sentry.MemoryCollectionData; import io.sentry.PerformanceCollectionData; import io.sentry.SentryLevel; +import io.sentry.SentryUUID; import io.sentry.android.core.internal.util.SentryFrameMetricsCollector; import io.sentry.profilemeasurements.ProfileMeasurement; import io.sentry.profilemeasurements.ProfileMeasurementValue; @@ -137,7 +138,7 @@ public AndroidProfiler( if (buildInfoProvider.getSdkInfoVersion() < Build.VERSION_CODES.LOLLIPOP) return null; // We create a file with a uuid name, so no need to check if it already exists - traceFile = new File(traceFilesDir, UUID.randomUUID() + ".trace"); + traceFile = new File(traceFilesDir, SentryUUID.generateSentryId() + ".trace"); measurementsMap.clear(); screenFrameRateMeasurements.clear(); diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/Installation.java b/sentry-android-core/src/main/java/io/sentry/android/core/Installation.java index 4c9b5ddbc20..08e9e34822c 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/Installation.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/Installation.java @@ -2,6 +2,7 @@ import android.content.Context; import io.sentry.ISentryLifecycleToken; +import io.sentry.SentryUUID; import io.sentry.util.AutoClosableReentrantLock; import java.io.File; import java.io.FileOutputStream; @@ -65,7 +66,7 @@ public static String id(final @NotNull Context context) throws RuntimeException static @NotNull String writeInstallationFile(final @NotNull File installation) throws IOException { try (final OutputStream out = new FileOutputStream(installation)) { - final String id = UUID.randomUUID().toString(); + final String id = SentryUUID.generateSentryId(); out.write(id.getBytes(UTF_8)); out.flush(); return id; diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/SentryFrameMetricsCollector.java b/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/SentryFrameMetricsCollector.java index 25ff5da2bdb..3a76cad40f9 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/SentryFrameMetricsCollector.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/SentryFrameMetricsCollector.java @@ -16,6 +16,7 @@ import io.sentry.ILogger; import io.sentry.SentryLevel; import io.sentry.SentryOptions; +import io.sentry.SentryUUID; import io.sentry.android.core.BuildInfoProvider; import io.sentry.android.core.ContextUtils; import io.sentry.util.Objects; @@ -262,7 +263,7 @@ public void onActivityDestroyed(@NotNull Activity activity) {} if (!isAvailable) { return null; } - final String uid = UUID.randomUUID().toString(); + final String uid = SentryUUID.generateSentryId(); listenerMap.put(uid, listener); trackCurrentWindow(); return uid; diff --git a/sentry/src/main/java/io/sentry/ProfilingTraceData.java b/sentry/src/main/java/io/sentry/ProfilingTraceData.java index 17332b5931c..2bcdb555004 100644 --- a/sentry/src/main/java/io/sentry/ProfilingTraceData.java +++ b/sentry/src/main/java/io/sentry/ProfilingTraceData.java @@ -154,7 +154,7 @@ public ProfilingTraceData( // Stacktrace context this.transactionId = transactionId; this.traceId = traceId; - this.profileId = UUID.randomUUID().toString(); + this.profileId = SentryUUID.generateSentryId(); this.environment = environment != null ? environment : DEFAULT_ENVIRONMENT; this.truncationReason = truncationReason; if (!isTruncationReasonValid()) { diff --git a/sentry/src/main/java/io/sentry/SpanId.java b/sentry/src/main/java/io/sentry/SpanId.java index 7857919e023..7eeb23a571f 100644 --- a/sentry/src/main/java/io/sentry/SpanId.java +++ b/sentry/src/main/java/io/sentry/SpanId.java @@ -3,14 +3,12 @@ import static io.sentry.util.StringUtils.PROPER_NIL_UUID; import io.sentry.util.LazyEvaluator; -import io.sentry.util.StringUtils; import java.io.IOException; import java.util.Objects; -import java.util.UUID; import org.jetbrains.annotations.NotNull; public final class SpanId implements JsonSerializable { - public static final SpanId EMPTY_ID = new SpanId(PROPER_NIL_UUID); + public static final SpanId EMPTY_ID = new SpanId(PROPER_NIL_UUID.replace("-", "")); private final @NotNull LazyEvaluator lazyValue; @@ -21,11 +19,7 @@ public SpanId(final @NotNull String value) { public SpanId() { this.lazyValue = - new LazyEvaluator<>( - () -> - StringUtils.normalizeUUID(UUID.randomUUID().toString()) - .replace("-", "") - .substring(0, 16)); + new LazyEvaluator<>(SentryUUID::generateSpanId); } @Override diff --git a/sentry/src/main/java/io/sentry/cache/EnvelopeCache.java b/sentry/src/main/java/io/sentry/cache/EnvelopeCache.java index 0255d05a18e..142776fa513 100644 --- a/sentry/src/main/java/io/sentry/cache/EnvelopeCache.java +++ b/sentry/src/main/java/io/sentry/cache/EnvelopeCache.java @@ -16,6 +16,7 @@ import io.sentry.SentryItemType; import io.sentry.SentryLevel; import io.sentry.SentryOptions; +import io.sentry.SentryUUID; import io.sentry.Session; import io.sentry.UncaughtExceptionHandlerIntegration; import io.sentry.hints.AbnormalExit; @@ -368,7 +369,7 @@ public void discard(final @NotNull SentryEnvelope envelope) { if (fileNameMap.containsKey(envelope)) { fileName = fileNameMap.get(envelope); } else { - fileName = UUID.randomUUID() + SUFFIX_ENVELOPE_FILE; + fileName = SentryUUID.generateSentryId() + SUFFIX_ENVELOPE_FILE; fileNameMap.put(envelope, fileName); } diff --git a/sentry/src/main/java/io/sentry/protocol/SentryId.java b/sentry/src/main/java/io/sentry/protocol/SentryId.java index bf1bde0c4dc..bb304978eeb 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryId.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryId.java @@ -5,6 +5,7 @@ import io.sentry.JsonSerializable; import io.sentry.ObjectReader; import io.sentry.ObjectWriter; +import io.sentry.SentryUUID; import io.sentry.util.LazyEvaluator; import io.sentry.util.StringUtils; import java.io.IOException; @@ -14,7 +15,7 @@ public final class SentryId implements JsonSerializable { - public static final SentryId EMPTY_ID = new SentryId(new UUID(0, 0)); + public static final SentryId EMPTY_ID = new SentryId(StringUtils.PROPER_NIL_UUID); private final @NotNull LazyEvaluator lazyStringValue; @@ -26,7 +27,7 @@ public SentryId(@Nullable UUID uuid) { if (uuid != null) { this.lazyStringValue = new LazyEvaluator<>(() -> normalize(uuid.toString())); } else { - this.lazyStringValue = new LazyEvaluator<>(() -> normalize(UUID.randomUUID().toString())); + this.lazyStringValue = new LazyEvaluator<>(SentryUUID::generateSentryId); } } From 6418a334b4063e0b7c637e6fb3076e08caf54d92 Mon Sep 17 00:00:00 2001 From: Sentry Github Bot Date: Wed, 23 Oct 2024 14:26:06 +0000 Subject: [PATCH 14/24] Format code --- .../sentry/android/core/AndroidProfiler.java | 1 - .../io/sentry/android/core/Installation.java | 1 - .../util/SentryFrameMetricsCollector.java | 1 - .../java/io/sentry/ProfilingTraceData.java | 1 - .../src/main/java/io/sentry/SentryUUID.java | 93 +++++++++---------- sentry/src/main/java/io/sentry/SpanId.java | 3 +- .../java/io/sentry/cache/EnvelopeCache.java | 1 - 7 files changed, 44 insertions(+), 57 deletions(-) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidProfiler.java b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidProfiler.java index 03d0501926a..46a50e46a0b 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidProfiler.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidProfiler.java @@ -25,7 +25,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.UUID; import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/Installation.java b/sentry-android-core/src/main/java/io/sentry/android/core/Installation.java index 08e9e34822c..ba08e71342a 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/Installation.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/Installation.java @@ -10,7 +10,6 @@ import java.io.OutputStream; import java.io.RandomAccessFile; import java.nio.charset.Charset; -import java.util.UUID; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/SentryFrameMetricsCollector.java b/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/SentryFrameMetricsCollector.java index 3a76cad40f9..ef509228dc6 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/SentryFrameMetricsCollector.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/SentryFrameMetricsCollector.java @@ -24,7 +24,6 @@ import java.lang.reflect.Field; import java.util.Map; import java.util.Set; -import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.TimeUnit; diff --git a/sentry/src/main/java/io/sentry/ProfilingTraceData.java b/sentry/src/main/java/io/sentry/ProfilingTraceData.java index 2bcdb555004..ac713ae84c2 100644 --- a/sentry/src/main/java/io/sentry/ProfilingTraceData.java +++ b/sentry/src/main/java/io/sentry/ProfilingTraceData.java @@ -10,7 +10,6 @@ import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.UUID; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import org.jetbrains.annotations.ApiStatus; diff --git a/sentry/src/main/java/io/sentry/SentryUUID.java b/sentry/src/main/java/io/sentry/SentryUUID.java index 9dc9719a9fb..ffd97083213 100644 --- a/sentry/src/main/java/io/sentry/SentryUUID.java +++ b/sentry/src/main/java/io/sentry/SentryUUID.java @@ -5,14 +5,14 @@ import java.util.UUID; /** - * Utility class for faster id generation for SentryId, SpanId, and unique filenames uses throughout the SDK - * It uses our vendored Random class instead of SecureRandom for improved performance - * It directly creates a correctly formatted String to be used as IDs in the Sentry context. + * Utility class for faster id generation for SentryId, SpanId, and unique filenames uses throughout + * the SDK It uses our vendored Random class instead of SecureRandom for improved performance It + * directly creates a correctly formatted String to be used as IDs in the Sentry context. * - * Id generation is sped up by 4 to 10 times based on the underlying java version. + *

Id generation is sped up by 4 to 10 times based on the underlying java version. * - * Based on the work of Jon Chambers - * Here: https://github.com/jchambers/fast-uuid + *

Based on the work of Jon Chambers Here: + * https://github.com/jchambers/fast-uuid */ public class SentryUUID { @@ -21,7 +21,7 @@ public class SentryUUID { private static final int SENTRY_SPAN_UUID_STRING_LENGTH = 16; private static final char[] HEX_DIGITS = - new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + new char[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; private static final long[] HEX_VALUES = new long[128]; @@ -59,22 +59,20 @@ private SentryUUID() { } /** - * Parses a UUID from the given character sequence. The character sequence must represent a UUID as described in - * {@link UUID#toString()}. + * Parses a UUID from the given character sequence. The character sequence must represent a UUID + * as described in {@link UUID#toString()}. * * @param uuidSequence the character sequence from which to parse a UUID - * * @return the UUID represented by the given character sequence - * - * @throws IllegalArgumentException if the given character sequence does not conform to the string representation as - * described in {@link UUID#toString()} + * @throws IllegalArgumentException if the given character sequence does not conform to the string + * representation as described in {@link UUID#toString()} */ public static UUID parseUUID(final CharSequence uuidSequence) { - if (uuidSequence.length() != UUID_STRING_LENGTH || - uuidSequence.charAt(8) != '-' || - uuidSequence.charAt(13) != '-' || - uuidSequence.charAt(18) != '-' || - uuidSequence.charAt(23) != '-') { + if (uuidSequence.length() != UUID_STRING_LENGTH + || uuidSequence.charAt(8) != '-' + || uuidSequence.charAt(13) != '-' + || uuidSequence.charAt(18) != '-' + || uuidSequence.charAt(23) != '-') { throw new IllegalArgumentException("Illegal UUID string: " + uuidSequence); } @@ -128,11 +126,10 @@ public static String generateSpanId() { } /** - * Returns a string representation of the given UUID. The returned string is formatted as described in - * {@link UUID#toString()}. + * Returns a string representation of the given UUID. The returned string is formatted as + * described in {@link UUID#toString()}. * * @param uuid the UUID to represent as a string - * * @return a string representation of the given UUID */ public static String toString(final UUID uuid) { @@ -142,16 +139,16 @@ public static String toString(final UUID uuid) { final char[] uuidChars = new char[UUID_STRING_LENGTH]; - uuidChars[0] = HEX_DIGITS[(int) ((mostSignificantBits & 0xf000000000000000L) >>> 60)]; - uuidChars[1] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0f00000000000000L) >>> 56)]; - uuidChars[2] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00f0000000000000L) >>> 52)]; - uuidChars[3] = HEX_DIGITS[(int) ((mostSignificantBits & 0x000f000000000000L) >>> 48)]; - uuidChars[4] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0000f00000000000L) >>> 44)]; - uuidChars[5] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00000f0000000000L) >>> 40)]; - uuidChars[6] = HEX_DIGITS[(int) ((mostSignificantBits & 0x000000f000000000L) >>> 36)]; - uuidChars[7] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0000000f00000000L) >>> 32)]; - uuidChars[8] = '-'; - uuidChars[9] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00000000f0000000L) >>> 28)]; + uuidChars[0] = HEX_DIGITS[(int) ((mostSignificantBits & 0xf000000000000000L) >>> 60)]; + uuidChars[1] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0f00000000000000L) >>> 56)]; + uuidChars[2] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00f0000000000000L) >>> 52)]; + uuidChars[3] = HEX_DIGITS[(int) ((mostSignificantBits & 0x000f000000000000L) >>> 48)]; + uuidChars[4] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0000f00000000000L) >>> 44)]; + uuidChars[5] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00000f0000000000L) >>> 40)]; + uuidChars[6] = HEX_DIGITS[(int) ((mostSignificantBits & 0x000000f000000000L) >>> 36)]; + uuidChars[7] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0000000f00000000L) >>> 32)]; + uuidChars[8] = '-'; + uuidChars[9] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00000000f0000000L) >>> 28)]; uuidChars[10] = HEX_DIGITS[(int) ((mostSignificantBits & 0x000000000f000000L) >>> 24)]; uuidChars[11] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0000000000f00000L) >>> 20)]; uuidChars[12] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00000000000f0000L) >>> 16)]; @@ -159,7 +156,7 @@ public static String toString(final UUID uuid) { uuidChars[14] = HEX_DIGITS[(int) ((mostSignificantBits & 0x000000000000f000L) >>> 12)]; uuidChars[15] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0000000000000f00L) >>> 8)]; uuidChars[16] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00000000000000f0L) >>> 4)]; - uuidChars[17] = HEX_DIGITS[(int) (mostSignificantBits & 0x000000000000000fL)]; + uuidChars[17] = HEX_DIGITS[(int) (mostSignificantBits & 0x000000000000000fL)]; uuidChars[18] = '-'; uuidChars[19] = HEX_DIGITS[(int) ((leastSignificantBits & 0xf000000000000000L) >>> 60)]; uuidChars[20] = HEX_DIGITS[(int) ((leastSignificantBits & 0x0f00000000000000L) >>> 56)]; @@ -177,7 +174,7 @@ public static String toString(final UUID uuid) { uuidChars[32] = HEX_DIGITS[(int) ((leastSignificantBits & 0x000000000000f000L) >>> 12)]; uuidChars[33] = HEX_DIGITS[(int) ((leastSignificantBits & 0x0000000000000f00L) >>> 8)]; uuidChars[34] = HEX_DIGITS[(int) ((leastSignificantBits & 0x00000000000000f0L) >>> 4)]; - uuidChars[35] = HEX_DIGITS[(int) (leastSignificantBits & 0x000000000000000fL)]; + uuidChars[35] = HEX_DIGITS[(int) (leastSignificantBits & 0x000000000000000fL)]; return new String(uuidChars); } @@ -210,7 +207,7 @@ public static String toSentryIdString(long mostSignificantBits, long leastSignif uuidChars[28] = HEX_DIGITS[(int) ((leastSignificantBits & 0x000000000000f000L) >>> 12)]; uuidChars[29] = HEX_DIGITS[(int) ((leastSignificantBits & 0x0000000000000f00L) >>> 8)]; uuidChars[30] = HEX_DIGITS[(int) ((leastSignificantBits & 0x00000000000000f0L) >>> 4)]; - uuidChars[31] = HEX_DIGITS[(int) (leastSignificantBits & 0x000000000000000fL)]; + uuidChars[31] = HEX_DIGITS[(int) (leastSignificantBits & 0x000000000000000fL)]; return new String(uuidChars); } @@ -219,7 +216,6 @@ public static String toSentrySpanIdString(final UUID uuid) { final long mostSignificantBits = uuid.getMostSignificantBits(); return toSentrySpanIdString(mostSignificantBits); - } public static String toSentrySpanIdString(long mostSignificantBits) { @@ -230,7 +226,8 @@ public static String toSentrySpanIdString(long mostSignificantBits) { return new String(uuidChars); } - private static void fillMostSignificantBits(final char[] uuidChars, final long mostSignificantBits) { + private static void fillMostSignificantBits( + final char[] uuidChars, final long mostSignificantBits) { uuidChars[0] = HEX_DIGITS[(int) ((mostSignificantBits & 0xf000000000000000L) >>> 60)]; uuidChars[1] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0f00000000000000L) >>> 56)]; uuidChars[2] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00f0000000000000L) >>> 52)]; @@ -265,13 +262,12 @@ public static long randomHalfLengthUUID() { Random random = SentryUUID.Holder.numberGenerator; byte[] randomBytes = new byte[8]; random.nextBytes(randomBytes); - randomBytes[6] &= 0x0f; /* clear version */ - randomBytes[6] |= 0x40; /* set to version 4 */ + randomBytes[6] &= 0x0f; /* clear version */ + randomBytes[6] |= 0x40; /* set to version 4 */ long msb = 0; - for (int i=0; i<8; i++) - msb = (msb << 8) | (randomBytes[i] & 0xff); + for (int i = 0; i < 8; i++) msb = (msb << 8) | (randomBytes[i] & 0xff); return msb; } @@ -280,19 +276,17 @@ public static UUID randomUUID() { Random random = SentryUUID.Holder.numberGenerator; byte[] randomBytes = new byte[16]; random.nextBytes(randomBytes); - randomBytes[6] &= 0x0f; /* clear version */ - randomBytes[6] |= 0x40; /* set to version 4 */ - randomBytes[8] &= 0x3f; /* clear variant */ - randomBytes[8] |= (byte) 0x80; /* set to IETF variant */ + randomBytes[6] &= 0x0f; /* clear version */ + randomBytes[6] |= 0x40; /* set to version 4 */ + randomBytes[8] &= 0x3f; /* clear variant */ + randomBytes[8] |= (byte) 0x80; /* set to IETF variant */ long msb = 0; long lsb = 0; - for (int i=0; i<8; i++) - msb = (msb << 8) | (randomBytes[i] & 0xff); + for (int i = 0; i < 8; i++) msb = (msb << 8) | (randomBytes[i] & 0xff); - for (int i=8; i<16; i++) - lsb = (lsb << 8) | (randomBytes[i] & 0xff); + for (int i = 8; i < 16; i++) lsb = (lsb << 8) | (randomBytes[i] & 0xff); return new UUID(msb, lsb); } @@ -300,7 +294,6 @@ public static UUID randomUUID() { private static class Holder { static final Random numberGenerator = new Random(); - private Holder() { - } + private Holder() {} } } diff --git a/sentry/src/main/java/io/sentry/SpanId.java b/sentry/src/main/java/io/sentry/SpanId.java index 7eeb23a571f..fef847253b5 100644 --- a/sentry/src/main/java/io/sentry/SpanId.java +++ b/sentry/src/main/java/io/sentry/SpanId.java @@ -18,8 +18,7 @@ public SpanId(final @NotNull String value) { } public SpanId() { - this.lazyValue = - new LazyEvaluator<>(SentryUUID::generateSpanId); + this.lazyValue = new LazyEvaluator<>(SentryUUID::generateSpanId); } @Override diff --git a/sentry/src/main/java/io/sentry/cache/EnvelopeCache.java b/sentry/src/main/java/io/sentry/cache/EnvelopeCache.java index 142776fa513..8117002daf9 100644 --- a/sentry/src/main/java/io/sentry/cache/EnvelopeCache.java +++ b/sentry/src/main/java/io/sentry/cache/EnvelopeCache.java @@ -46,7 +46,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.UUID; import java.util.WeakHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; From 5314e83570400c37f9ce4317eae787992fa92610 Mon Sep 17 00:00:00 2001 From: Lukas Bloder Date: Tue, 29 Oct 2024 15:02:24 +0100 Subject: [PATCH 15/24] split SentryUUID internals into separate classes for easier attribution --- sentry/api/sentry.api | 19 ++ .../src/main/java/io/sentry/SentryUUID.java | 290 +----------------- .../java/io/sentry/util/UUIDGenerator.java | 77 +++++ .../java/io/sentry/util/UUIDStringUtils.java | 141 +++++++++ 4 files changed, 244 insertions(+), 283 deletions(-) create mode 100644 sentry/src/main/java/io/sentry/util/UUIDGenerator.java create mode 100644 sentry/src/main/java/io/sentry/util/UUIDStringUtils.java diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 293ea6cb8ca..2b2cbfed650 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -3265,6 +3265,11 @@ public final class io/sentry/SentryTracer : io/sentry/ITransaction { public fun updateEndDate (Lio/sentry/SentryDate;)Z } +public final class io/sentry/SentryUUID { + public static fun generateSentryId ()Ljava/lang/String; + public static fun generateSpanId ()Ljava/lang/String; +} + public final class io/sentry/SentryWrapper { public fun ()V public static fun wrapCallable (Ljava/util/concurrent/Callable;)Ljava/util/concurrent/Callable; @@ -6180,6 +6185,20 @@ public final class io/sentry/util/TracingUtils$TracingHeaders { public fun getSentryTraceHeader ()Lio/sentry/SentryTraceHeader; } +public final class io/sentry/util/UUIDGenerator { + public fun ()V + public static fun randomHalfLengthUUID ()J + public static fun randomUUID ()Ljava/util/UUID; +} + +public final class io/sentry/util/UUIDStringUtils { + public fun ()V + public static fun toSentryIdString (JJ)Ljava/lang/String; + public static fun toSentryIdString (Ljava/util/UUID;)Ljava/lang/String; + public static fun toSentrySpanIdString (J)Ljava/lang/String; + public static fun toSentrySpanIdString (Ljava/util/UUID;)Ljava/lang/String; +} + public final class io/sentry/util/UrlUtils { public static final field SENSITIVE_DATA_SUBSTITUTE Ljava/lang/String; public fun ()V diff --git a/sentry/src/main/java/io/sentry/SentryUUID.java b/sentry/src/main/java/io/sentry/SentryUUID.java index ffd97083213..14050fd86a4 100644 --- a/sentry/src/main/java/io/sentry/SentryUUID.java +++ b/sentry/src/main/java/io/sentry/SentryUUID.java @@ -1,299 +1,23 @@ package io.sentry; -import java.util.Arrays; -import java.util.Random; -import java.util.UUID; +import io.sentry.util.UUIDGenerator; +import io.sentry.util.UUIDStringUtils; /** - * Utility class for faster id generation for SentryId, SpanId, and unique filenames uses throughout - * the SDK It uses our vendored Random class instead of SecureRandom for improved performance It - * directly creates a correctly formatted String to be used as IDs in the Sentry context. - * - *

Id generation is sped up by 4 to 10 times based on the underlying java version. - * - *

Based on the work of Jon Chambers Here: - * https://github.com/jchambers/fast-uuid + * SentryUUID is a utility class for generating Sentry-specific ID Strings. It provides methods for + * generating Sentry IDs and Sentry Span IDs. */ -public class SentryUUID { - - private static final int UUID_STRING_LENGTH = 36; - private static final int SENTRY_UUID_STRING_LENGTH = 32; - private static final int SENTRY_SPAN_UUID_STRING_LENGTH = 16; - - private static final char[] HEX_DIGITS = - new char[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; - - private static final long[] HEX_VALUES = new long[128]; - - static { - Arrays.fill(HEX_VALUES, -1); - - HEX_VALUES['0'] = 0x0; - HEX_VALUES['1'] = 0x1; - HEX_VALUES['2'] = 0x2; - HEX_VALUES['3'] = 0x3; - HEX_VALUES['4'] = 0x4; - HEX_VALUES['5'] = 0x5; - HEX_VALUES['6'] = 0x6; - HEX_VALUES['7'] = 0x7; - HEX_VALUES['8'] = 0x8; - HEX_VALUES['9'] = 0x9; - - HEX_VALUES['a'] = 0xa; - HEX_VALUES['b'] = 0xb; - HEX_VALUES['c'] = 0xc; - HEX_VALUES['d'] = 0xd; - HEX_VALUES['e'] = 0xe; - HEX_VALUES['f'] = 0xf; - - HEX_VALUES['A'] = 0xa; - HEX_VALUES['B'] = 0xb; - HEX_VALUES['C'] = 0xc; - HEX_VALUES['D'] = 0xd; - HEX_VALUES['E'] = 0xe; - HEX_VALUES['F'] = 0xf; - } +public final class SentryUUID { private SentryUUID() { // A private constructor prevents callers from accidentally instantiating FastUUID objects } - /** - * Parses a UUID from the given character sequence. The character sequence must represent a UUID - * as described in {@link UUID#toString()}. - * - * @param uuidSequence the character sequence from which to parse a UUID - * @return the UUID represented by the given character sequence - * @throws IllegalArgumentException if the given character sequence does not conform to the string - * representation as described in {@link UUID#toString()} - */ - public static UUID parseUUID(final CharSequence uuidSequence) { - if (uuidSequence.length() != UUID_STRING_LENGTH - || uuidSequence.charAt(8) != '-' - || uuidSequence.charAt(13) != '-' - || uuidSequence.charAt(18) != '-' - || uuidSequence.charAt(23) != '-') { - - throw new IllegalArgumentException("Illegal UUID string: " + uuidSequence); - } - - long mostSignificantBits = getHexValueForChar(uuidSequence.charAt(0)) << 60; - mostSignificantBits |= getHexValueForChar(uuidSequence.charAt(1)) << 56; - mostSignificantBits |= getHexValueForChar(uuidSequence.charAt(2)) << 52; - mostSignificantBits |= getHexValueForChar(uuidSequence.charAt(3)) << 48; - mostSignificantBits |= getHexValueForChar(uuidSequence.charAt(4)) << 44; - mostSignificantBits |= getHexValueForChar(uuidSequence.charAt(5)) << 40; - mostSignificantBits |= getHexValueForChar(uuidSequence.charAt(6)) << 36; - mostSignificantBits |= getHexValueForChar(uuidSequence.charAt(7)) << 32; - - mostSignificantBits |= getHexValueForChar(uuidSequence.charAt(9)) << 28; - mostSignificantBits |= getHexValueForChar(uuidSequence.charAt(10)) << 24; - mostSignificantBits |= getHexValueForChar(uuidSequence.charAt(11)) << 20; - mostSignificantBits |= getHexValueForChar(uuidSequence.charAt(12)) << 16; - - mostSignificantBits |= getHexValueForChar(uuidSequence.charAt(14)) << 12; - mostSignificantBits |= getHexValueForChar(uuidSequence.charAt(15)) << 8; - mostSignificantBits |= getHexValueForChar(uuidSequence.charAt(16)) << 4; - mostSignificantBits |= getHexValueForChar(uuidSequence.charAt(17)); - - long leastSignificantBits = getHexValueForChar(uuidSequence.charAt(19)) << 60; - leastSignificantBits |= getHexValueForChar(uuidSequence.charAt(20)) << 56; - leastSignificantBits |= getHexValueForChar(uuidSequence.charAt(21)) << 52; - leastSignificantBits |= getHexValueForChar(uuidSequence.charAt(22)) << 48; - - leastSignificantBits |= getHexValueForChar(uuidSequence.charAt(24)) << 44; - leastSignificantBits |= getHexValueForChar(uuidSequence.charAt(25)) << 40; - leastSignificantBits |= getHexValueForChar(uuidSequence.charAt(26)) << 36; - leastSignificantBits |= getHexValueForChar(uuidSequence.charAt(27)) << 32; - leastSignificantBits |= getHexValueForChar(uuidSequence.charAt(28)) << 28; - leastSignificantBits |= getHexValueForChar(uuidSequence.charAt(29)) << 24; - leastSignificantBits |= getHexValueForChar(uuidSequence.charAt(30)) << 20; - leastSignificantBits |= getHexValueForChar(uuidSequence.charAt(31)) << 16; - leastSignificantBits |= getHexValueForChar(uuidSequence.charAt(32)) << 12; - leastSignificantBits |= getHexValueForChar(uuidSequence.charAt(33)) << 8; - leastSignificantBits |= getHexValueForChar(uuidSequence.charAt(34)) << 4; - leastSignificantBits |= getHexValueForChar(uuidSequence.charAt(35)); - - return new UUID(mostSignificantBits, leastSignificantBits); - } - public static String generateSentryId() { - return toSentryIdString(SentryUUID.randomUUID()); + return UUIDStringUtils.toSentryIdString(UUIDGenerator.randomUUID()); } public static String generateSpanId() { - return toSentrySpanIdString(SentryUUID.randomHalfLengthUUID()); - } - - /** - * Returns a string representation of the given UUID. The returned string is formatted as - * described in {@link UUID#toString()}. - * - * @param uuid the UUID to represent as a string - * @return a string representation of the given UUID - */ - public static String toString(final UUID uuid) { - - final long mostSignificantBits = uuid.getMostSignificantBits(); - final long leastSignificantBits = uuid.getLeastSignificantBits(); - - final char[] uuidChars = new char[UUID_STRING_LENGTH]; - - uuidChars[0] = HEX_DIGITS[(int) ((mostSignificantBits & 0xf000000000000000L) >>> 60)]; - uuidChars[1] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0f00000000000000L) >>> 56)]; - uuidChars[2] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00f0000000000000L) >>> 52)]; - uuidChars[3] = HEX_DIGITS[(int) ((mostSignificantBits & 0x000f000000000000L) >>> 48)]; - uuidChars[4] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0000f00000000000L) >>> 44)]; - uuidChars[5] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00000f0000000000L) >>> 40)]; - uuidChars[6] = HEX_DIGITS[(int) ((mostSignificantBits & 0x000000f000000000L) >>> 36)]; - uuidChars[7] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0000000f00000000L) >>> 32)]; - uuidChars[8] = '-'; - uuidChars[9] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00000000f0000000L) >>> 28)]; - uuidChars[10] = HEX_DIGITS[(int) ((mostSignificantBits & 0x000000000f000000L) >>> 24)]; - uuidChars[11] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0000000000f00000L) >>> 20)]; - uuidChars[12] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00000000000f0000L) >>> 16)]; - uuidChars[13] = '-'; - uuidChars[14] = HEX_DIGITS[(int) ((mostSignificantBits & 0x000000000000f000L) >>> 12)]; - uuidChars[15] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0000000000000f00L) >>> 8)]; - uuidChars[16] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00000000000000f0L) >>> 4)]; - uuidChars[17] = HEX_DIGITS[(int) (mostSignificantBits & 0x000000000000000fL)]; - uuidChars[18] = '-'; - uuidChars[19] = HEX_DIGITS[(int) ((leastSignificantBits & 0xf000000000000000L) >>> 60)]; - uuidChars[20] = HEX_DIGITS[(int) ((leastSignificantBits & 0x0f00000000000000L) >>> 56)]; - uuidChars[21] = HEX_DIGITS[(int) ((leastSignificantBits & 0x00f0000000000000L) >>> 52)]; - uuidChars[22] = HEX_DIGITS[(int) ((leastSignificantBits & 0x000f000000000000L) >>> 48)]; - uuidChars[23] = '-'; - uuidChars[24] = HEX_DIGITS[(int) ((leastSignificantBits & 0x0000f00000000000L) >>> 44)]; - uuidChars[25] = HEX_DIGITS[(int) ((leastSignificantBits & 0x00000f0000000000L) >>> 40)]; - uuidChars[26] = HEX_DIGITS[(int) ((leastSignificantBits & 0x000000f000000000L) >>> 36)]; - uuidChars[27] = HEX_DIGITS[(int) ((leastSignificantBits & 0x0000000f00000000L) >>> 32)]; - uuidChars[28] = HEX_DIGITS[(int) ((leastSignificantBits & 0x00000000f0000000L) >>> 28)]; - uuidChars[29] = HEX_DIGITS[(int) ((leastSignificantBits & 0x000000000f000000L) >>> 24)]; - uuidChars[30] = HEX_DIGITS[(int) ((leastSignificantBits & 0x0000000000f00000L) >>> 20)]; - uuidChars[31] = HEX_DIGITS[(int) ((leastSignificantBits & 0x00000000000f0000L) >>> 16)]; - uuidChars[32] = HEX_DIGITS[(int) ((leastSignificantBits & 0x000000000000f000L) >>> 12)]; - uuidChars[33] = HEX_DIGITS[(int) ((leastSignificantBits & 0x0000000000000f00L) >>> 8)]; - uuidChars[34] = HEX_DIGITS[(int) ((leastSignificantBits & 0x00000000000000f0L) >>> 4)]; - uuidChars[35] = HEX_DIGITS[(int) (leastSignificantBits & 0x000000000000000fL)]; - - return new String(uuidChars); - } - - public static String toSentryIdString(final UUID uuid) { - - final long mostSignificantBits = uuid.getMostSignificantBits(); - final long leastSignificantBits = uuid.getLeastSignificantBits(); - - return toSentryIdString(mostSignificantBits, leastSignificantBits); - } - - public static String toSentryIdString(long mostSignificantBits, long leastSignificantBits) { - final char[] uuidChars = new char[SENTRY_UUID_STRING_LENGTH]; - - fillMostSignificantBits(uuidChars, mostSignificantBits); - - uuidChars[16] = HEX_DIGITS[(int) ((leastSignificantBits & 0xf000000000000000L) >>> 60)]; - uuidChars[17] = HEX_DIGITS[(int) ((leastSignificantBits & 0x0f00000000000000L) >>> 56)]; - uuidChars[18] = HEX_DIGITS[(int) ((leastSignificantBits & 0x00f0000000000000L) >>> 52)]; - uuidChars[19] = HEX_DIGITS[(int) ((leastSignificantBits & 0x000f000000000000L) >>> 48)]; - uuidChars[20] = HEX_DIGITS[(int) ((leastSignificantBits & 0x0000f00000000000L) >>> 44)]; - uuidChars[21] = HEX_DIGITS[(int) ((leastSignificantBits & 0x00000f0000000000L) >>> 40)]; - uuidChars[22] = HEX_DIGITS[(int) ((leastSignificantBits & 0x000000f000000000L) >>> 36)]; - uuidChars[23] = HEX_DIGITS[(int) ((leastSignificantBits & 0x0000000f00000000L) >>> 32)]; - uuidChars[24] = HEX_DIGITS[(int) ((leastSignificantBits & 0x00000000f0000000L) >>> 28)]; - uuidChars[25] = HEX_DIGITS[(int) ((leastSignificantBits & 0x000000000f000000L) >>> 24)]; - uuidChars[26] = HEX_DIGITS[(int) ((leastSignificantBits & 0x0000000000f00000L) >>> 20)]; - uuidChars[27] = HEX_DIGITS[(int) ((leastSignificantBits & 0x00000000000f0000L) >>> 16)]; - uuidChars[28] = HEX_DIGITS[(int) ((leastSignificantBits & 0x000000000000f000L) >>> 12)]; - uuidChars[29] = HEX_DIGITS[(int) ((leastSignificantBits & 0x0000000000000f00L) >>> 8)]; - uuidChars[30] = HEX_DIGITS[(int) ((leastSignificantBits & 0x00000000000000f0L) >>> 4)]; - uuidChars[31] = HEX_DIGITS[(int) (leastSignificantBits & 0x000000000000000fL)]; - - return new String(uuidChars); - } - - public static String toSentrySpanIdString(final UUID uuid) { - - final long mostSignificantBits = uuid.getMostSignificantBits(); - return toSentrySpanIdString(mostSignificantBits); - } - - public static String toSentrySpanIdString(long mostSignificantBits) { - final char[] uuidChars = new char[SENTRY_SPAN_UUID_STRING_LENGTH]; - - fillMostSignificantBits(uuidChars, mostSignificantBits); - - return new String(uuidChars); - } - - private static void fillMostSignificantBits( - final char[] uuidChars, final long mostSignificantBits) { - uuidChars[0] = HEX_DIGITS[(int) ((mostSignificantBits & 0xf000000000000000L) >>> 60)]; - uuidChars[1] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0f00000000000000L) >>> 56)]; - uuidChars[2] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00f0000000000000L) >>> 52)]; - uuidChars[3] = HEX_DIGITS[(int) ((mostSignificantBits & 0x000f000000000000L) >>> 48)]; - uuidChars[4] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0000f00000000000L) >>> 44)]; - uuidChars[5] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00000f0000000000L) >>> 40)]; - uuidChars[6] = HEX_DIGITS[(int) ((mostSignificantBits & 0x000000f000000000L) >>> 36)]; - uuidChars[7] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0000000f00000000L) >>> 32)]; - uuidChars[8] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00000000f0000000L) >>> 28)]; - uuidChars[9] = HEX_DIGITS[(int) ((mostSignificantBits & 0x000000000f000000L) >>> 24)]; - uuidChars[10] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0000000000f00000L) >>> 20)]; - uuidChars[11] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00000000000f0000L) >>> 16)]; - uuidChars[12] = HEX_DIGITS[(int) ((mostSignificantBits & 0x000000000000f000L) >>> 12)]; - uuidChars[13] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0000000000000f00L) >>> 8)]; - uuidChars[14] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00000000000000f0L) >>> 4)]; - uuidChars[15] = HEX_DIGITS[(int) (mostSignificantBits & 0x000000000000000fL)]; - } - - static long getHexValueForChar(final char c) { - try { - if (HEX_VALUES[c] < 0) { - throw new IllegalArgumentException("Illegal hexadecimal digit: " + c); - } - } catch (final ArrayIndexOutOfBoundsException e) { - throw new IllegalArgumentException("Illegal hexadecimal digit: " + c); - } - - return HEX_VALUES[c]; - } - - public static long randomHalfLengthUUID() { - Random random = SentryUUID.Holder.numberGenerator; - byte[] randomBytes = new byte[8]; - random.nextBytes(randomBytes); - randomBytes[6] &= 0x0f; /* clear version */ - randomBytes[6] |= 0x40; /* set to version 4 */ - - long msb = 0; - - for (int i = 0; i < 8; i++) msb = (msb << 8) | (randomBytes[i] & 0xff); - - return msb; - } - - public static UUID randomUUID() { - Random random = SentryUUID.Holder.numberGenerator; - byte[] randomBytes = new byte[16]; - random.nextBytes(randomBytes); - randomBytes[6] &= 0x0f; /* clear version */ - randomBytes[6] |= 0x40; /* set to version 4 */ - randomBytes[8] &= 0x3f; /* clear variant */ - randomBytes[8] |= (byte) 0x80; /* set to IETF variant */ - - long msb = 0; - long lsb = 0; - - for (int i = 0; i < 8; i++) msb = (msb << 8) | (randomBytes[i] & 0xff); - - for (int i = 8; i < 16; i++) lsb = (lsb << 8) | (randomBytes[i] & 0xff); - - return new UUID(msb, lsb); - } - - private static class Holder { - static final Random numberGenerator = new Random(); - - private Holder() {} + return UUIDStringUtils.toSentrySpanIdString(UUIDGenerator.randomHalfLengthUUID()); } } diff --git a/sentry/src/main/java/io/sentry/util/UUIDGenerator.java b/sentry/src/main/java/io/sentry/util/UUIDGenerator.java new file mode 100644 index 00000000000..857b21e183d --- /dev/null +++ b/sentry/src/main/java/io/sentry/util/UUIDGenerator.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package io.sentry.util; + +import java.util.Random; +import java.util.UUID; + +/** + * Utility class for generating UUIDs and half-length (1 long) UUIDs. Adapted from `java.util.UUID` + * to use a faster random number generator. + */ +public final class UUIDGenerator { + + @SuppressWarnings("NarrowingCompoundAssignment") + public static long randomHalfLengthUUID() { + Random random = Holder.numberGenerator; + byte[] randomBytes = new byte[8]; + random.nextBytes(randomBytes); + randomBytes[6] &= 0x0f; /* clear version */ + randomBytes[6] |= 0x40; /* set to version 4 */ + + long msb = 0; + + for (int i = 0; i < 8; i++) msb = (msb << 8) | (randomBytes[i] & 0xff); + + return msb; + } + + @SuppressWarnings("NarrowingCompoundAssignment") + public static UUID randomUUID() { + Random random = Holder.numberGenerator; + byte[] randomBytes = new byte[16]; + random.nextBytes(randomBytes); + randomBytes[6] &= 0x0f; /* clear version */ + randomBytes[6] |= 0x40; /* set to version 4 */ + randomBytes[8] &= 0x3f; /* clear variant */ + randomBytes[8] |= (byte) 0x80; /* set to IETF variant */ + + long msb = 0; + long lsb = 0; + + for (int i = 0; i < 8; i++) msb = (msb << 8) | (randomBytes[i] & 0xff); + + for (int i = 8; i < 16; i++) lsb = (lsb << 8) | (randomBytes[i] & 0xff); + + return new UUID(msb, lsb); + } + + private static class Holder { + static final Random numberGenerator = new Random(); + + private Holder() {} + } +} diff --git a/sentry/src/main/java/io/sentry/util/UUIDStringUtils.java b/sentry/src/main/java/io/sentry/util/UUIDStringUtils.java new file mode 100644 index 00000000000..ffff5a8155a --- /dev/null +++ b/sentry/src/main/java/io/sentry/util/UUIDStringUtils.java @@ -0,0 +1,141 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Jon Chambers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package io.sentry.util; + +import java.util.Arrays; +import java.util.UUID; + +/** + * Utility class to convert UUIDs and longs to Sentry ID Strings + * + *

Adapted from Jon Chambers' work here. + */ +public final class UUIDStringUtils { + + private static final int SENTRY_UUID_STRING_LENGTH = 32; + private static final int SENTRY_SPAN_UUID_STRING_LENGTH = 16; + + private static final char[] HEX_DIGITS = + new char[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + private static final long[] HEX_VALUES = new long[128]; + + static { + Arrays.fill(HEX_VALUES, -1); + + HEX_VALUES['0'] = 0x0; + HEX_VALUES['1'] = 0x1; + HEX_VALUES['2'] = 0x2; + HEX_VALUES['3'] = 0x3; + HEX_VALUES['4'] = 0x4; + HEX_VALUES['5'] = 0x5; + HEX_VALUES['6'] = 0x6; + HEX_VALUES['7'] = 0x7; + HEX_VALUES['8'] = 0x8; + HEX_VALUES['9'] = 0x9; + + HEX_VALUES['a'] = 0xa; + HEX_VALUES['b'] = 0xb; + HEX_VALUES['c'] = 0xc; + HEX_VALUES['d'] = 0xd; + HEX_VALUES['e'] = 0xe; + HEX_VALUES['f'] = 0xf; + + HEX_VALUES['A'] = 0xa; + HEX_VALUES['B'] = 0xb; + HEX_VALUES['C'] = 0xc; + HEX_VALUES['D'] = 0xd; + HEX_VALUES['E'] = 0xe; + HEX_VALUES['F'] = 0xf; + } + + public static String toSentryIdString(final UUID uuid) { + + final long mostSignificantBits = uuid.getMostSignificantBits(); + final long leastSignificantBits = uuid.getLeastSignificantBits(); + + return toSentryIdString(mostSignificantBits, leastSignificantBits); + } + + public static String toSentryIdString(long mostSignificantBits, long leastSignificantBits) { + final char[] uuidChars = new char[SENTRY_UUID_STRING_LENGTH]; + + fillMostSignificantBits(uuidChars, mostSignificantBits); + + uuidChars[16] = HEX_DIGITS[(int) ((leastSignificantBits & 0xf000000000000000L) >>> 60)]; + uuidChars[17] = HEX_DIGITS[(int) ((leastSignificantBits & 0x0f00000000000000L) >>> 56)]; + uuidChars[18] = HEX_DIGITS[(int) ((leastSignificantBits & 0x00f0000000000000L) >>> 52)]; + uuidChars[19] = HEX_DIGITS[(int) ((leastSignificantBits & 0x000f000000000000L) >>> 48)]; + uuidChars[20] = HEX_DIGITS[(int) ((leastSignificantBits & 0x0000f00000000000L) >>> 44)]; + uuidChars[21] = HEX_DIGITS[(int) ((leastSignificantBits & 0x00000f0000000000L) >>> 40)]; + uuidChars[22] = HEX_DIGITS[(int) ((leastSignificantBits & 0x000000f000000000L) >>> 36)]; + uuidChars[23] = HEX_DIGITS[(int) ((leastSignificantBits & 0x0000000f00000000L) >>> 32)]; + uuidChars[24] = HEX_DIGITS[(int) ((leastSignificantBits & 0x00000000f0000000L) >>> 28)]; + uuidChars[25] = HEX_DIGITS[(int) ((leastSignificantBits & 0x000000000f000000L) >>> 24)]; + uuidChars[26] = HEX_DIGITS[(int) ((leastSignificantBits & 0x0000000000f00000L) >>> 20)]; + uuidChars[27] = HEX_DIGITS[(int) ((leastSignificantBits & 0x00000000000f0000L) >>> 16)]; + uuidChars[28] = HEX_DIGITS[(int) ((leastSignificantBits & 0x000000000000f000L) >>> 12)]; + uuidChars[29] = HEX_DIGITS[(int) ((leastSignificantBits & 0x0000000000000f00L) >>> 8)]; + uuidChars[30] = HEX_DIGITS[(int) ((leastSignificantBits & 0x00000000000000f0L) >>> 4)]; + uuidChars[31] = HEX_DIGITS[(int) (leastSignificantBits & 0x000000000000000fL)]; + + return new String(uuidChars); + } + + public static String toSentrySpanIdString(final UUID uuid) { + + final long mostSignificantBits = uuid.getMostSignificantBits(); + return toSentrySpanIdString(mostSignificantBits); + } + + public static String toSentrySpanIdString(long mostSignificantBits) { + final char[] uuidChars = new char[SENTRY_SPAN_UUID_STRING_LENGTH]; + + fillMostSignificantBits(uuidChars, mostSignificantBits); + + return new String(uuidChars); + } + + private static void fillMostSignificantBits( + final char[] uuidChars, final long mostSignificantBits) { + uuidChars[0] = HEX_DIGITS[(int) ((mostSignificantBits & 0xf000000000000000L) >>> 60)]; + uuidChars[1] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0f00000000000000L) >>> 56)]; + uuidChars[2] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00f0000000000000L) >>> 52)]; + uuidChars[3] = HEX_DIGITS[(int) ((mostSignificantBits & 0x000f000000000000L) >>> 48)]; + uuidChars[4] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0000f00000000000L) >>> 44)]; + uuidChars[5] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00000f0000000000L) >>> 40)]; + uuidChars[6] = HEX_DIGITS[(int) ((mostSignificantBits & 0x000000f000000000L) >>> 36)]; + uuidChars[7] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0000000f00000000L) >>> 32)]; + uuidChars[8] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00000000f0000000L) >>> 28)]; + uuidChars[9] = HEX_DIGITS[(int) ((mostSignificantBits & 0x000000000f000000L) >>> 24)]; + uuidChars[10] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0000000000f00000L) >>> 20)]; + uuidChars[11] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00000000000f0000L) >>> 16)]; + uuidChars[12] = HEX_DIGITS[(int) ((mostSignificantBits & 0x000000000000f000L) >>> 12)]; + uuidChars[13] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0000000000000f00L) >>> 8)]; + uuidChars[14] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00000000000000f0L) >>> 4)]; + uuidChars[15] = HEX_DIGITS[(int) (mostSignificantBits & 0x000000000000000fL)]; + } +} From 097738b0916b9b094bf9dd6837cd94c413cd32ab Mon Sep 17 00:00:00 2001 From: Lukas Bloder Date: Tue, 29 Oct 2024 15:25:24 +0100 Subject: [PATCH 16/24] add changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3444dcb7cd..2eed1926f2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ - `globalHubMode` used to only be a param on `Sentry.init`. To make it easier to be used in e.g. Desktop environments, we now additionally added it as an option on SentryOptions that can also be set via `sentry.properties`. - If both the param on `Sentry.init` and the option are set, the option will win. By default the option is set to `null` meaning whatever is passed to `Sentry.init` takes effect. - Lazy uuid generation for SentryId and SpanId ([#3770](https://github.com/getsentry/sentry-java/pull/3770)) +- Faster generation of Sentry and Span IDs ([#3818](https://github.com/getsentry/sentry-java/pull/3818)) + - Uses faster implementation to convert UUID to SentryID String + - Uses faster Random implementation to generate UUIDs ## 8.0.0-beta.1 From d11c3ccb19d41ab2443baef541fd2aba4e182649 Mon Sep 17 00:00:00 2001 From: Lukas Bloder Date: Tue, 29 Oct 2024 16:22:09 +0100 Subject: [PATCH 17/24] fix tests --- .../java/io/sentry/protocol/SentryIdTest.kt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/sentry/src/test/java/io/sentry/protocol/SentryIdTest.kt b/sentry/src/test/java/io/sentry/protocol/SentryIdTest.kt index f26f5911560..b979947f133 100644 --- a/sentry/src/test/java/io/sentry/protocol/SentryIdTest.kt +++ b/sentry/src/test/java/io/sentry/protocol/SentryIdTest.kt @@ -1,11 +1,11 @@ package io.sentry.protocol +import io.sentry.SentryUUID import io.sentry.util.StringUtils import org.mockito.Mockito import org.mockito.kotlin.any import org.mockito.kotlin.never import org.mockito.kotlin.times -import java.util.* import kotlin.test.Test import kotlin.test.assertEquals @@ -19,25 +19,25 @@ class SentryIdTest { @Test fun `UUID is not generated on initialization`() { - val uuid = UUID.randomUUID() - Mockito.mockStatic(UUID::class.java).use { utils -> - utils.`when` { UUID.randomUUID() }.thenReturn(uuid) + val uuid = SentryUUID.generateSentryId() + Mockito.mockStatic(SentryUUID::class.java).use { utils -> + utils.`when` { SentryUUID.generateSentryId() }.thenReturn(uuid) val ignored = SentryId() - utils.verify({ UUID.randomUUID() }, never()) + utils.verify({ SentryUUID.generateSentryId() }, never()) } } @Test fun `UUID is generated only once`() { - val uuid = UUID.randomUUID() - Mockito.mockStatic(UUID::class.java).use { utils -> - utils.`when` { UUID.randomUUID() }.thenReturn(uuid) + val uuid = SentryUUID.generateSentryId() + Mockito.mockStatic(SentryUUID::class.java).use { utils -> + utils.`when` { SentryUUID.generateSentryId() }.thenReturn(uuid) val sentryId = SentryId() val uuid1 = sentryId.toString() val uuid2 = sentryId.toString() assertEquals(uuid1, uuid2) - utils.verify({ UUID.randomUUID() }, times(1)) + utils.verify({ SentryUUID.generateSentryId() }, times(1)) } } From 81a3bdbe97d08da4c2d1b5f1622149a1dc1d579d Mon Sep 17 00:00:00 2001 From: Lukas Bloder Date: Tue, 29 Oct 2024 19:34:49 +0100 Subject: [PATCH 18/24] fix SpanId.EMPTY_ID --- sentry/src/main/java/io/sentry/SpanId.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sentry/src/main/java/io/sentry/SpanId.java b/sentry/src/main/java/io/sentry/SpanId.java index fef847253b5..fcc7f3a4f38 100644 --- a/sentry/src/main/java/io/sentry/SpanId.java +++ b/sentry/src/main/java/io/sentry/SpanId.java @@ -8,7 +8,8 @@ import org.jetbrains.annotations.NotNull; public final class SpanId implements JsonSerializable { - public static final SpanId EMPTY_ID = new SpanId(PROPER_NIL_UUID.replace("-", "")); + public static final SpanId EMPTY_ID = + new SpanId(PROPER_NIL_UUID.replace("-", "").substring(0, 16)); private final @NotNull LazyEvaluator lazyValue; From fac412e65a456e023be2a395e11bd24874a350ba Mon Sep 17 00:00:00 2001 From: Lukas Bloder Date: Fri, 8 Nov 2024 11:38:06 +0100 Subject: [PATCH 19/24] test normalized is never called in no-arg constructor, only called once when invoking with string --- .../java/io/sentry/protocol/SentryId.java | 6 ++-- .../java/io/sentry/protocol/SentryIdTest.kt | 29 ++++++++++++++++++- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/sentry/src/main/java/io/sentry/protocol/SentryId.java b/sentry/src/main/java/io/sentry/protocol/SentryId.java index bb304978eeb..6789ec70550 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryId.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryId.java @@ -10,6 +10,8 @@ import io.sentry.util.StringUtils; import java.io.IOException; import java.util.UUID; + +import io.sentry.util.UUIDStringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -25,7 +27,7 @@ public SentryId() { public SentryId(@Nullable UUID uuid) { if (uuid != null) { - this.lazyStringValue = new LazyEvaluator<>(() -> normalize(uuid.toString())); + this.lazyStringValue = new LazyEvaluator<>(() -> normalize(UUIDStringUtils.toSentryIdString(uuid))); } else { this.lazyStringValue = new LazyEvaluator<>(SentryUUID::generateSentryId); } @@ -39,7 +41,7 @@ public SentryId(final @NotNull String sentryIdString) { + "or 36 characters long (completed UUID). Received: " + sentryIdString); } - this.lazyStringValue = new LazyEvaluator<>(() -> normalize(normalized)); + this.lazyStringValue = new LazyEvaluator<>(() -> normalized); } @Override diff --git a/sentry/src/test/java/io/sentry/protocol/SentryIdTest.kt b/sentry/src/test/java/io/sentry/protocol/SentryIdTest.kt index b979947f133..38fc7599579 100644 --- a/sentry/src/test/java/io/sentry/protocol/SentryIdTest.kt +++ b/sentry/src/test/java/io/sentry/protocol/SentryIdTest.kt @@ -6,6 +6,7 @@ import org.mockito.Mockito import org.mockito.kotlin.any import org.mockito.kotlin.never import org.mockito.kotlin.times +import java.util.* import kotlin.test.Test import kotlin.test.assertEquals @@ -42,13 +43,39 @@ class SentryIdTest { } @Test - fun `normalizeUUID is only called once`() { + fun `normalizeUUID is never called when using empty constructor`() { Mockito.mockStatic(StringUtils::class.java).use { utils -> utils.`when` { StringUtils.normalizeUUID(any()) }.thenReturn("00000000000000000000000000000000") val sentryId = SentryId() val uuid1 = sentryId.toString() val uuid2 = sentryId.toString() + assertEquals(uuid1, uuid2) + utils.verify({ StringUtils.normalizeUUID(any()) }, times(0)) + } + } + + @Test + fun `normalizeUUID is only called once when String is passed to constructor`() { + Mockito.mockStatic(StringUtils::class.java).use { utils -> + utils.`when` { StringUtils.normalizeUUID(any()) }.thenReturn("00000000000000000000000000000000") + val sentryId = SentryId("00000000000000000000000000000000") + val uuid1 = sentryId.toString() + val uuid2 = sentryId.toString() + + assertEquals(uuid1, uuid2) + utils.verify({ StringUtils.normalizeUUID(any()) }, times(1)) + } + } + + @Test + fun `normalizeUUID is only called once when UUID is passed to constructor`() { + Mockito.mockStatic(StringUtils::class.java).use { utils -> + utils.`when` { StringUtils.normalizeUUID(any()) }.thenReturn("00000000000000000000000000000000") + val sentryId = SentryId(UUID.randomUUID()) + val uuid1 = sentryId.toString() + val uuid2 = sentryId.toString() + assertEquals(uuid1, uuid2) utils.verify({ StringUtils.normalizeUUID(any()) }, times(1)) } From 30b260213e25d391fe113cc1ccf655c8a850d1e3 Mon Sep 17 00:00:00 2001 From: Lukas Bloder Date: Fri, 8 Nov 2024 11:53:53 +0100 Subject: [PATCH 20/24] fix test --- .../sentry-uitest-android-critical/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry-android-integration-tests/sentry-uitest-android-critical/build.gradle.kts b/sentry-android-integration-tests/sentry-uitest-android-critical/build.gradle.kts index cebf744a24d..da7add25cc9 100644 --- a/sentry-android-integration-tests/sentry-uitest-android-critical/build.gradle.kts +++ b/sentry-android-integration-tests/sentry-uitest-android-critical/build.gradle.kts @@ -17,7 +17,7 @@ android { defaultConfig { applicationId = "io.sentry.uitest.android.critical" - minSdk = Config.Android.minSdkVersionCompose + minSdk = Config.Android.minSdkVersion targetSdk = Config.Android.targetSdkVersion versionCode = 1 versionName = "1.0" From 05540a3613dd8108f29103778925369962e93428 Mon Sep 17 00:00:00 2001 From: Sentry Github Bot Date: Fri, 8 Nov 2024 10:57:09 +0000 Subject: [PATCH 21/24] Format code --- sentry/src/main/java/io/sentry/protocol/SentryId.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sentry/src/main/java/io/sentry/protocol/SentryId.java b/sentry/src/main/java/io/sentry/protocol/SentryId.java index 6789ec70550..15db6bc60ce 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryId.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryId.java @@ -8,10 +8,9 @@ import io.sentry.SentryUUID; import io.sentry.util.LazyEvaluator; import io.sentry.util.StringUtils; +import io.sentry.util.UUIDStringUtils; import java.io.IOException; import java.util.UUID; - -import io.sentry.util.UUIDStringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -27,7 +26,8 @@ public SentryId() { public SentryId(@Nullable UUID uuid) { if (uuid != null) { - this.lazyStringValue = new LazyEvaluator<>(() -> normalize(UUIDStringUtils.toSentryIdString(uuid))); + this.lazyStringValue = + new LazyEvaluator<>(() -> normalize(UUIDStringUtils.toSentryIdString(uuid))); } else { this.lazyStringValue = new LazyEvaluator<>(SentryUUID::generateSentryId); } From 143c26eb2e1c2a0cbada0a1425003a13a1e65556 Mon Sep 17 00:00:00 2001 From: Lukas Bloder Date: Fri, 8 Nov 2024 11:57:50 +0100 Subject: [PATCH 22/24] use new Random Generator --- .../src/main/java/io/sentry/util/UUIDGenerator.java | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/sentry/src/main/java/io/sentry/util/UUIDGenerator.java b/sentry/src/main/java/io/sentry/util/UUIDGenerator.java index 857b21e183d..cbe90f96d83 100644 --- a/sentry/src/main/java/io/sentry/util/UUIDGenerator.java +++ b/sentry/src/main/java/io/sentry/util/UUIDGenerator.java @@ -25,7 +25,6 @@ package io.sentry.util; -import java.util.Random; import java.util.UUID; /** @@ -36,7 +35,7 @@ public final class UUIDGenerator { @SuppressWarnings("NarrowingCompoundAssignment") public static long randomHalfLengthUUID() { - Random random = Holder.numberGenerator; + Random random = SentryRandom.current(); byte[] randomBytes = new byte[8]; random.nextBytes(randomBytes); randomBytes[6] &= 0x0f; /* clear version */ @@ -51,7 +50,7 @@ public static long randomHalfLengthUUID() { @SuppressWarnings("NarrowingCompoundAssignment") public static UUID randomUUID() { - Random random = Holder.numberGenerator; + Random random = SentryRandom.current(); byte[] randomBytes = new byte[16]; random.nextBytes(randomBytes); randomBytes[6] &= 0x0f; /* clear version */ @@ -68,10 +67,4 @@ public static UUID randomUUID() { return new UUID(msb, lsb); } - - private static class Holder { - static final Random numberGenerator = new Random(); - - private Holder() {} - } } From 45c3cbe24cb113de5b68348893d39f8124e73dea Mon Sep 17 00:00:00 2001 From: Lukas Bloder Date: Fri, 8 Nov 2024 12:38:19 +0100 Subject: [PATCH 23/24] fix Sentry Empty ID, replace dashes if 36 char uuid String is passed to SentryId constructor --- sentry/src/main/java/io/sentry/protocol/SentryId.java | 9 +++++++-- sentry/src/test/java/io/sentry/protocol/SentryIdTest.kt | 7 +++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/sentry/src/main/java/io/sentry/protocol/SentryId.java b/sentry/src/main/java/io/sentry/protocol/SentryId.java index 15db6bc60ce..6bd6391aef2 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryId.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryId.java @@ -16,7 +16,8 @@ public final class SentryId implements JsonSerializable { - public static final SentryId EMPTY_ID = new SentryId(StringUtils.PROPER_NIL_UUID); + public static final SentryId EMPTY_ID = + new SentryId(StringUtils.PROPER_NIL_UUID.replace("-", "")); private final @NotNull LazyEvaluator lazyStringValue; @@ -41,7 +42,11 @@ public SentryId(final @NotNull String sentryIdString) { + "or 36 characters long (completed UUID). Received: " + sentryIdString); } - this.lazyStringValue = new LazyEvaluator<>(() -> normalized); + if (normalized.length() == 36) { + this.lazyStringValue = new LazyEvaluator<>(() -> normalized.replace("-", "")); + } else { + this.lazyStringValue = new LazyEvaluator<>(() -> normalized); + } } @Override diff --git a/sentry/src/test/java/io/sentry/protocol/SentryIdTest.kt b/sentry/src/test/java/io/sentry/protocol/SentryIdTest.kt index 38fc7599579..a517185e1bb 100644 --- a/sentry/src/test/java/io/sentry/protocol/SentryIdTest.kt +++ b/sentry/src/test/java/io/sentry/protocol/SentryIdTest.kt @@ -18,6 +18,13 @@ class SentryIdTest { assertEquals("00000000000000000000000000000000", id.toString()) } + @Test + fun `dashes are stripped if initialized with 36char uuid string`() { + val uuidString = UUID.randomUUID().toString() + val id = SentryId(uuidString) + assertEquals(uuidString.replace("-", ""), id.toString()) + } + @Test fun `UUID is not generated on initialization`() { val uuid = SentryUUID.generateSentryId() From 5c2c580058657a8183588a8f9fe76b2ebf12dd66 Mon Sep 17 00:00:00 2001 From: Lukas Bloder Date: Fri, 8 Nov 2024 15:10:40 +0100 Subject: [PATCH 24/24] fix Tests for SpanId, add tests for SentryUUID and UUIDStringUtils --- .../java/io/sentry/protocol/SentryId.java | 2 +- .../src/test/java/io/sentry/SentryUUIDTest.kt | 19 ++++++++++ .../java/io/sentry/UUIDStringUtilsTest.kt | 23 ++++++++++++ .../java/io/sentry/protocol/SpanIdTest.kt | 37 ++++++------------- 4 files changed, 54 insertions(+), 27 deletions(-) create mode 100644 sentry/src/test/java/io/sentry/SentryUUIDTest.kt create mode 100644 sentry/src/test/java/io/sentry/UUIDStringUtilsTest.kt diff --git a/sentry/src/main/java/io/sentry/protocol/SentryId.java b/sentry/src/main/java/io/sentry/protocol/SentryId.java index 6bd6391aef2..a5bd7980c3f 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryId.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryId.java @@ -43,7 +43,7 @@ public SentryId(final @NotNull String sentryIdString) { + sentryIdString); } if (normalized.length() == 36) { - this.lazyStringValue = new LazyEvaluator<>(() -> normalized.replace("-", "")); + this.lazyStringValue = new LazyEvaluator<>(() -> normalize(normalized)); } else { this.lazyStringValue = new LazyEvaluator<>(() -> normalized); } diff --git a/sentry/src/test/java/io/sentry/SentryUUIDTest.kt b/sentry/src/test/java/io/sentry/SentryUUIDTest.kt new file mode 100644 index 00000000000..cdb8b92684a --- /dev/null +++ b/sentry/src/test/java/io/sentry/SentryUUIDTest.kt @@ -0,0 +1,19 @@ +package io.sentry + +import junit.framework.TestCase.assertEquals +import kotlin.test.Test + +class SentryUUIDTest { + + @Test + fun `generated SentryID is 32 characters long`() { + val sentryId = SentryUUID.generateSentryId() + assertEquals(32, sentryId.length) + } + + @Test + fun `generated SpanID is 16 characters long`() { + val sentryId = SentryUUID.generateSpanId() + assertEquals(16, sentryId.length) + } +} diff --git a/sentry/src/test/java/io/sentry/UUIDStringUtilsTest.kt b/sentry/src/test/java/io/sentry/UUIDStringUtilsTest.kt new file mode 100644 index 00000000000..c6a1901d93d --- /dev/null +++ b/sentry/src/test/java/io/sentry/UUIDStringUtilsTest.kt @@ -0,0 +1,23 @@ +package io.sentry + +import io.sentry.util.UUIDStringUtils +import java.util.UUID +import kotlin.test.Test +import kotlin.test.assertEquals + +class UUIDStringUtilsTest { + + @Test + fun `UUID toString matches UUIDStringUtils to String`() { + val uuid = UUID.randomUUID() + val sentryIdString = uuid.toString().replace("-", "") + assertEquals(sentryIdString, UUIDStringUtils.toSentryIdString(uuid)) + } + + @Test + fun `UUID toString matches UUIDStringUtils to String for SpanId`() { + val uuid = UUID.randomUUID() + val sentryIdString = uuid.toString().replace("-", "").substring(0, 16) + assertEquals(sentryIdString, UUIDStringUtils.toSentrySpanIdString(uuid)) + } +} diff --git a/sentry/src/test/java/io/sentry/protocol/SpanIdTest.kt b/sentry/src/test/java/io/sentry/protocol/SpanIdTest.kt index 071d402e2fa..f21a9d59309 100644 --- a/sentry/src/test/java/io/sentry/protocol/SpanIdTest.kt +++ b/sentry/src/test/java/io/sentry/protocol/SpanIdTest.kt @@ -1,51 +1,36 @@ package io.sentry.protocol +import io.sentry.SentryUUID import io.sentry.SpanId -import io.sentry.util.StringUtils import org.mockito.Mockito -import org.mockito.kotlin.any import org.mockito.kotlin.never import org.mockito.kotlin.times -import java.util.* import kotlin.test.Test import kotlin.test.assertEquals class SpanIdTest { @Test - fun `UUID is not generated on initialization`() { - val uuid = UUID.randomUUID() - Mockito.mockStatic(UUID::class.java).use { utils -> - utils.`when` { UUID.randomUUID() }.thenReturn(uuid) + fun `ID is not generated on initialization`() { + val uuid = SentryUUID.generateSpanId() + Mockito.mockStatic(SentryUUID::class.java).use { utils -> + utils.`when` { SentryUUID.generateSpanId() }.thenReturn(uuid) val ignored = SpanId() - utils.verify({ UUID.randomUUID() }, never()) + utils.verify({ SentryUUID.generateSpanId() }, never()) } } @Test - fun `UUID is generated only once`() { - val uuid = UUID.randomUUID() - Mockito.mockStatic(java.util.UUID::class.java).use { utils -> - utils.`when` { UUID.randomUUID() }.thenReturn(uuid) + fun `ID is generated only once`() { + val uuid = SentryUUID.generateSpanId() + Mockito.mockStatic(SentryUUID::class.java).use { utils -> + utils.`when` { SentryUUID.generateSpanId() }.thenReturn(uuid) val spanId = SpanId() val uuid1 = spanId.toString() val uuid2 = spanId.toString() assertEquals(uuid1, uuid2) - utils.verify({ UUID.randomUUID() }, times(1)) - } - } - - @Test - fun `normalizeUUID is only called once`() { - Mockito.mockStatic(StringUtils::class.java).use { utils -> - utils.`when` { StringUtils.normalizeUUID(any()) }.thenReturn("00000000000000000000000000000000") - val spanId = SpanId() - val uuid1 = spanId.toString() - val uuid2 = spanId.toString() - - assertEquals(uuid1, uuid2) - utils.verify({ StringUtils.normalizeUUID(any()) }, times(1)) + utils.verify({ SentryUUID.generateSpanId() }, times(1)) } } }