diff --git a/CHANGELOG.md b/CHANGELOG.md index ec08b2ae2de..c5a04ffc9e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - Auxiliary information (such as current memory load) at the time of ANR event. - If you would like us to provide support for the old approach working alongside the new one on Android 11 and above (e.g. for raising events for slow code on main thread), consider upvoting [this issue](https://github.com/getsentry/sentry-java/issues/2693). - The old watchdog implementation will continue working for older API versions (Android < 11) +- Open up transaction methods ([#2701](https://github.com/getsentry/sentry-java/pull/2701)) ### Fixes diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 769f64fc76b..197cedc5400 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -620,6 +620,7 @@ public abstract interface class io/sentry/ITransaction : io/sentry/ISpan { public abstract fun setContext (Ljava/lang/String;Ljava/lang/Object;)V public abstract fun setName (Ljava/lang/String;)V public abstract fun setName (Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;)V + public abstract fun startChild (Ljava/lang/String;Ljava/lang/String;Lio/sentry/SentryDate;)Lio/sentry/ISpan; } public abstract interface class io/sentry/ITransactionProfiler { @@ -924,6 +925,7 @@ public final class io/sentry/NoOpTransaction : io/sentry/ITransaction { public fun setThrowable (Ljava/lang/Throwable;)V public fun startChild (Ljava/lang/String;)Lio/sentry/ISpan; public fun startChild (Ljava/lang/String;Ljava/lang/String;)Lio/sentry/ISpan; + public fun startChild (Ljava/lang/String;Ljava/lang/String;Lio/sentry/SentryDate;)Lio/sentry/ISpan; public fun startChild (Ljava/lang/String;Ljava/lang/String;Lio/sentry/SentryDate;Lio/sentry/Instrumenter;)Lio/sentry/ISpan; public fun startChild (Ljava/lang/String;Ljava/lang/String;Lio/sentry/SentryDate;Lio/sentry/Instrumenter;Lio/sentry/SpanOptions;)Lio/sentry/ISpan; public fun startChild (Ljava/lang/String;Ljava/lang/String;Lio/sentry/SpanOptions;)Lio/sentry/ISpan; @@ -1916,6 +1918,7 @@ public final class io/sentry/SentryTracer : io/sentry/ITransaction { public fun setThrowable (Ljava/lang/Throwable;)V public fun startChild (Ljava/lang/String;)Lio/sentry/ISpan; public fun startChild (Ljava/lang/String;Ljava/lang/String;)Lio/sentry/ISpan; + public fun startChild (Ljava/lang/String;Ljava/lang/String;Lio/sentry/SentryDate;)Lio/sentry/ISpan; public fun startChild (Ljava/lang/String;Ljava/lang/String;Lio/sentry/SentryDate;Lio/sentry/Instrumenter;)Lio/sentry/ISpan; public fun startChild (Ljava/lang/String;Ljava/lang/String;Lio/sentry/SentryDate;Lio/sentry/Instrumenter;Lio/sentry/SpanOptions;)Lio/sentry/ISpan; public fun startChild (Ljava/lang/String;Ljava/lang/String;Lio/sentry/SpanOptions;)Lio/sentry/ISpan; diff --git a/sentry/src/main/java/io/sentry/IHub.java b/sentry/src/main/java/io/sentry/IHub.java index 31cc1437972..b99e99ab82a 100644 --- a/sentry/src/main/java/io/sentry/IHub.java +++ b/sentry/src/main/java/io/sentry/IHub.java @@ -494,7 +494,15 @@ ITransaction startTransaction( return startTransaction(name, operation, null); } - @ApiStatus.Internal + /** + * Creates a Transaction and returns the instance. Based on the passed transaction context and + * transaction options the decision if transaction is sampled will be taken by {@link + * TracesSampler}. + * + * @param transactionContext the transaction context + * @param transactionOptions the transaction options + * @return created transaction. + */ @NotNull ITransaction startTransaction( final @NotNull TransactionContext transactionContext, diff --git a/sentry/src/main/java/io/sentry/ITransaction.java b/sentry/src/main/java/io/sentry/ITransaction.java index 29478511ec4..127dc448ee4 100644 --- a/sentry/src/main/java/io/sentry/ITransaction.java +++ b/sentry/src/main/java/io/sentry/ITransaction.java @@ -41,6 +41,18 @@ public interface ITransaction extends ISpan { @TestOnly List getSpans(); + /** + * Starts a child Span. + * + * @param operation - new span operation name + * @param description - the span description + * @param timestamp - the start timestamp of the span + * @return a new transaction span + */ + @NotNull + ISpan startChild( + @NotNull String operation, @Nullable String description, @Nullable SentryDate timestamp); + /** * Returns if transaction is sampled. * diff --git a/sentry/src/main/java/io/sentry/NoOpTransaction.java b/sentry/src/main/java/io/sentry/NoOpTransaction.java index cc2b0b54261..902c8a7da6c 100644 --- a/sentry/src/main/java/io/sentry/NoOpTransaction.java +++ b/sentry/src/main/java/io/sentry/NoOpTransaction.java @@ -82,6 +82,12 @@ public void setName(@NotNull String name, @NotNull TransactionNameSource transac return Collections.emptyList(); } + @Override + public @NotNull ISpan startChild( + @NotNull String operation, @Nullable String description, @Nullable SentryDate timestamp) { + return NoOpSpan.getInstance(); + } + @Override public @Nullable Span getLatestActiveSpan() { return null; diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index b4e31dd7733..3bf9fccb7fa 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -886,7 +886,6 @@ public static void endSession() { * @param transactionOptions options for the transaction * @return created transaction. */ - @ApiStatus.Internal public static @NotNull ITransaction startTransaction( final @NotNull TransactionContext transactionContext, final @NotNull TransactionOptions transactionOptions) { diff --git a/sentry/src/main/java/io/sentry/SentryTracer.java b/sentry/src/main/java/io/sentry/SentryTracer.java index 0ad89e8a209..d27499cb6c8 100644 --- a/sentry/src/main/java/io/sentry/SentryTracer.java +++ b/sentry/src/main/java/io/sentry/SentryTracer.java @@ -404,6 +404,14 @@ private ISpan createChild( return createChild(operation, description, timestamp, instrumenter, spanOptions); } + @Override + public @NotNull ISpan startChild( + final @NotNull String operation, + @Nullable String description, + @Nullable SentryDate timestamp) { + return createChild(operation, description, timestamp, Instrumenter.SENTRY, new SpanOptions()); + } + @Override public @NotNull ISpan startChild( final @NotNull String operation, final @Nullable String description) { diff --git a/sentry/src/main/java/io/sentry/TransactionOptions.java b/sentry/src/main/java/io/sentry/TransactionOptions.java index a3b1450d748..3362d979408 100644 --- a/sentry/src/main/java/io/sentry/TransactionOptions.java +++ b/sentry/src/main/java/io/sentry/TransactionOptions.java @@ -1,76 +1,149 @@ package io.sentry; -import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; -@ApiStatus.Internal +/** Sentry Transaction options */ public final class TransactionOptions extends SpanOptions { + /** + * Arbitrary data used in {@link SamplingContext} to determine if transaction is going to be + * sampled. + */ private @Nullable CustomSamplingContext customSamplingContext = null; + + /** Defines if transaction should be bound to scope */ private boolean bindToScope = false; + + /** The start timestamp of the transaction */ private @Nullable SentryDate startTimestamp = null; + + /** + * When `waitForChildren` is set to `true`, tracer will finish only when both conditions are met + * (the order of meeting condition does not matter): - tracer itself is finished - all child spans + * are finished. + */ private boolean waitForChildren = false; + /** + * The idle time, measured in ms, to wait until the transaction will be finished. The span will + * use the end timestamp of the last finished span as the endtime for the transaction. + * + *

When set to {@code null} the transaction must be finished manually. + * + *

The default is 3 seconds. + */ private @Nullable Long idleTimeout = null; + + /** + * When `waitForChildren` is set to `true` and this callback is set, it's called before the + * transaction is captured. + */ private @Nullable TransactionFinishedCallback transactionFinishedCallback = null; + /** + * Gets the customSamplingContext + * + * @return customSamplingContext - the customSamplingContext + */ public @Nullable CustomSamplingContext getCustomSamplingContext() { return customSamplingContext; } + /** + * Sets the customSamplingContext + * + * @param customSamplingContext - the customSamplingContext + */ public void setCustomSamplingContext(@Nullable CustomSamplingContext customSamplingContext) { this.customSamplingContext = customSamplingContext; } + /** + * Checks if bindToScope is enabled + * + * @return true if enabled or false otherwise + */ public boolean isBindToScope() { return bindToScope; } + /** + * Sets bindToScope to enabled or disabled + * + * @param bindToScope true if enabled or false otherwise + */ public void setBindToScope(boolean bindToScope) { this.bindToScope = bindToScope; } + /** + * Gets the startTimestamp + * + * @return startTimestamp - the startTimestamp + */ public @Nullable SentryDate getStartTimestamp() { return startTimestamp; } + /** + * Sets the startTimestamp + * + * @param startTimestamp - the startTimestamp + */ public void setStartTimestamp(@Nullable SentryDate startTimestamp) { this.startTimestamp = startTimestamp; } /** - * When `waitForChildren` is set to `true`, tracer will finish only when both conditions are met - * (the order of meeting condition does not matter): - tracer itself is finished - all child spans - * are finished. + * Checks if waitForChildren is enabled + * + * @return true if enabled or false otherwise */ public boolean isWaitForChildren() { return waitForChildren; } + /** + * Sets waitForChildren to enabled or disabled + * + * @param waitForChildren true if enabled or false otherwise + */ public void setWaitForChildren(boolean waitForChildren) { this.waitForChildren = waitForChildren; } /** - * The idle time, measured in ms, to wait until the transaction will be finished. The span will - * use the end timestamp of the last finished span as the endtime for the transaction. - * - *

When set to {@code null} the transaction must be finished manually. + * Gets the idleTimeout * - *

The default is 3 seconds. + * @return idleTimeout - the idleTimeout */ public @Nullable Long getIdleTimeout() { return idleTimeout; } + /** + * Sets the idleTimeout + * + * @param idleTimeout - the idleTimeout + */ public void setIdleTimeout(@Nullable Long idleTimeout) { this.idleTimeout = idleTimeout; } + /** + * Gets the transactionFinishedCallback callback + * + * @return transactionFinishedCallback - the transactionFinishedCallback callback + */ public @Nullable TransactionFinishedCallback getTransactionFinishedCallback() { return transactionFinishedCallback; } + /** + * Sets the transactionFinishedCallback callback + * + * @param transactionFinishedCallback - the transactionFinishedCallback callback + */ public void setTransactionFinishedCallback( @Nullable TransactionFinishedCallback transactionFinishedCallback) { this.transactionFinishedCallback = transactionFinishedCallback; diff --git a/sentry/src/test/java/io/sentry/NoOpTransactionTest.kt b/sentry/src/test/java/io/sentry/NoOpTransactionTest.kt index b228ae6f232..7c0ed522ed2 100644 --- a/sentry/src/test/java/io/sentry/NoOpTransactionTest.kt +++ b/sentry/src/test/java/io/sentry/NoOpTransactionTest.kt @@ -15,6 +15,10 @@ class NoOpTransactionTest { fun `startChild does not return null`() { assertNotNull(transaction.startChild("op")) assertNotNull(transaction.startChild("op", "desc")) + assertNotNull(transaction.startChild("op", "desc", SpanOptions())) + assertNotNull(transaction.startChild("op", "desc", SentryNanotimeDate())) + assertNotNull(transaction.startChild("op", "desc", SentryNanotimeDate(), Instrumenter.SENTRY)) + assertNotNull(transaction.startChild("op", "desc", SentryNanotimeDate(), Instrumenter.SENTRY, SpanOptions())) } @Test diff --git a/sentry/src/test/java/io/sentry/SentryTracerTest.kt b/sentry/src/test/java/io/sentry/SentryTracerTest.kt index fafe1f40090..6f9300a9076 100644 --- a/sentry/src/test/java/io/sentry/SentryTracerTest.kt +++ b/sentry/src/test/java/io/sentry/SentryTracerTest.kt @@ -328,6 +328,27 @@ class SentryTracerTest { assertEquals(tracer.root.traceId, span.traceId) } + @Test + fun `starting child with operation, description and timestamp creates a new span`() { + val tracer = fixture.getSut() + val sentryDate = SentryNanotimeDate() + val span = tracer.startChild("op", "description", sentryDate) as Span + assertNotNull(span) + assertNotNull(span.spanId) + assertNotNull(span.startDate) + assertEquals("op", span.operation) + assertEquals("description", span.description) + assertEquals(sentryDate, span.startDate) + } + + @Test + fun `sstarting child with operation, description and timestamp adds a span to transaction`() { + val tracer = fixture.getSut() + val span = tracer.startChild("op", "description", SentryNanotimeDate()) + assertEquals(1, tracer.children.size) + assertEquals(span, tracer.children.first()) + } + @Test fun `setting op sets op on TraceContext`() { val tracer = fixture.getSut()