Skip to content

Commit

Permalink
Merge f2fba85 into 6b638b0
Browse files Browse the repository at this point in the history
  • Loading branch information
lbloder authored Oct 18, 2024
2 parents 6b638b0 + f2fba85 commit ab1b958
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 42 deletions.
10 changes: 8 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -42,9 +48,9 @@
- If you are using `graphql-java` v21 or earlier, you can use the `sentry-graphql` module
- For `graphql-java` v22 and newer please use the `sentry-graphql-22` module
- We now provide a `SentryInstrumenter` bean directly for Spring (Boot) if there is none yet instead of using `GraphQlSourceBuilderCustomizer` to add the instrumentation ([#3744](https://github.com/getsentry/sentry-java/pull/3744))
- It is now also possible to provide a bean of type `SentryGraphqlInstrumentation.BeforeSpanCallback` which is then used by `SentryInstrumenter`
- 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
- Also does not emit `transaction.data` in `exras` anymore

### Fixes

Expand Down
1 change: 1 addition & 0 deletions sentry/api/sentry.api
Original file line number Diff line number Diff line change
Expand Up @@ -6147,6 +6147,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;
Expand Down
31 changes: 18 additions & 13 deletions sentry/src/main/java/io/sentry/SpanId.java
Original file line number Diff line number Diff line change
@@ -1,52 +1,57 @@
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<String> 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
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

@Override
public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger logger)
throws IOException {
writer.value(value);
writer.value(lazyValue.getValue());
}

// JsonElementDeserializer
Expand Down
49 changes: 23 additions & 26 deletions sentry/src/main/java/io/sentry/protocol/SentryId.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,69 +5,66 @@
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;
import org.jetbrains.annotations.NotNull;
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<String> lazyStringValue;

public SentryId() {
this((UUID) null);
}

public SentryId(@Nullable UUID uuid) {
if (uuid == null) {
uuid = UUID.randomUUID();
if (uuid != null) {
this.lazyStringValue = new LazyEvaluator<>(() -> uuidToSentryIdString(uuid));
} else {
this.lazyStringValue = new LazyEvaluator<>(() -> uuidToSentryIdString(UUID.randomUUID()));
}
this.uuid = uuid;
}

public SentryId(final @NotNull String sentryIdString) {
this.uuid = fromStringSentryId(StringUtils.normalizeUUID(sentryIdString));
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));
}

@Override
public String toString() {
return StringUtils.normalizeUUID(uuid.toString()).replace("-", "");
return lazyStringValue.getValue();
}

@Override
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 lazyStringValue.getValue().compareTo(sentryId.lazyStringValue.getValue()) == 0;
}

@Override
public int hashCode() {
return uuid.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();
}
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);
}
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
Expand Down
2 changes: 1 addition & 1 deletion sentry/src/main/java/io/sentry/util/StringUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -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() {}
Expand Down
43 changes: 43 additions & 0 deletions sentry/src/test/java/io/sentry/protocol/SentryIdTest.kt
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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> { 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> { 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`<Any> { 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))
}
}
}
51 changes: 51 additions & 0 deletions sentry/src/test/java/io/sentry/protocol/SpanIdTest.kt
Original file line number Diff line number Diff line change
@@ -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> { 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> { 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`<Any> { 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))
}
}
}

0 comments on commit ab1b958

Please sign in to comment.